ugrás a tartalomhoz

Asszociatív tömbből, asszociatív tömb, ami objektumokat tárol

whiteman0524 · 2010. Szep. 4. (Szo), 12.44
Üdv !

A címet sajnos nem tudtam jobban megfogalmazni, de majd mindjárt megpróbálom érthetőbben leírni a problémát :)

Szóval, adott egy asszociatív tömb, például egy ilyen :

$tomb = array(
	'elso'     => 1,
	'masodik'  => 2,
	'harmadik' => 5,
	'negyedik' => array(
		'elso'     => 1,
		'masodik'  => 2,
		'hamradik' => array(
			'elso'    => 5,
			'masodik' => 4,
		)
	)
);
Azt, szeretném elérni, hogy ennek a tömbnek, minden elemét átalakítom egy objektummá. Tehát például a $tomb['elso'] eleme, egy olyan objektum lesz, ami az 1 értéket tárolja, amit mondjuk a getValue() metódussal lehet elérni. A $tomb['negyedik'] eleme, pedig egy olyan objektum, aminek az értéke szintén egy tömb lesz, mégpedig az 'elso', 'masodik', és a 'harmadik' kulcsokkal, ahol minden kulcs szintén egy objektumot reprezentál az előzőeknek megfelelően.

Biztos iszonyat könnyű a feladat, de nekem valahogy nem sikerül a dolog. Próbáltam egy ilyen kóddal :

function map(&$array){
	foreach($array as $key => $value){
		$value = new Objektum($value);
		if(is_array($value->getValue())){
			map($value->getValue());
		} 
	}
}
..de itt azt a hibát kaptam, a 'map($value->getValue());' sorra, hogy 'Only variables should be passed by reference'. Szóval nem tudom hogyan lehetne ezt megoldani.

A segítséget előre is kösz.
 
1

Jah, és a végén...

whiteman0524 · 2010. Szep. 4. (Szo), 13.04
..azt szeretném elérni, hogy a tömb bármely elemét kezelhessem objektumként. Például $tomb['elso']->getValue(), vagy $tomb['negyedik']['harmadik']->getValue(), etc...

nem tudom hogy ez egyáltalán valahogy megoldható-e, ha így nem is. Arra gondoltam hogy minden Objektum elemnek, implementálnia kéne az ArrayAccess interfészt, és így talán lehetséges lenne a fenti szintaxis.
2

Ez már OO PHP és én nem

ironwill · 2010. Szep. 4. (Szo), 19.52
Ez már OO PHP és én nem vagyok igazán járatos benne, de azt gondolom, hogy a gyári Array-t kellene kiterjeszteni és saját függvénnyel kiegészíteni.

Épp ezért, ez nem a legjobb megoldás, de legalább valami.. :)

class arrayObj {
    // property declaration
    private $val;

    // constructor declaration
    public function __construct($val) {
        $this->val = $val;
    }
    
    // method declaration
    public function getValue() {
        return $this->val;
    }
    
    // method declaration
    public function setValue($val) {
        $this->val = $val;
    }
}

$tomb = array('elso' = new arrayObj(1), 'ketto' = new arrayObj(2));

echo "elso: ".$tomb['elso']->getValue();
Én legalábbis így oldanám meg.
3

class Tree { static public

inf · 2010. Szep. 4. (Szo), 20.15

class Tree
{
	static public function fromArray(&$array)
	{
		$result=new Tree();
		foreach($array as $key => &$value)
		{
			$result->put($key,is_array($value)?Tree::fromArray($value):$value);
			unset($value);
		}
		return $result;
	}
	
	public function __construct()
	{
		$this->source=array();
	}
	
	public function put($key,$value)
	{
		$this->source[$key]=$value;
	}
	
	public function get($key)
	{
		if (isset($this->source[$key]))
		{
			return $this->source[$key];
		}
	}
}

$tomb = array(  
     'elso'     => 1,  
     'masodik'  => 2,  
     'harmadik' => 5,  
     'negyedik' => array(  
        'elso'     => 1,  
        'masodik'  => 2,  
        'harmadik' => array(  
             'elso'    => 5,  
             'masodik' => 4,  
         )  
    )  
 );  
 
$tree=Tree::fromArray($tomb);
$value=$tree->get('negyedik')->get('harmadik')->get('elso');
echo $value; //5
Az a baj a tiéddel, hogy nem igazán érted, hogy a php hogy kezeli a változókat.
4

Köszi szépen a megoldásokat

whiteman0524 · 2010. Szep. 4. (Szo), 22.28
Mindkettőtöknek :) inf3rno, a te megoldásod jó, de nem a legszebb (az viszont igaz, hogy nem is ez volt a kérésem, hanem csupán egy megoldás) :) Közben sikerült megoldanom a problémát. Ugye az ArrayAccess interfészt mindenki jól ismeri, de ha mégsem, akkor a php oldalán rá lehet keresni. Szóval a megoldásom :

class Node implements ArrayAccess {

  private $container;

  public function __construct($input_value){
    if(is_array($input_value)){
      $this->container = array();
      foreach($input_value as $key => $value){
        $this->container[$key] = new Node($value);
      }
    } else {
      $this->container = $input_value;
    }
  }

  // A következő metódusok az ArrayAccess interfész implementálásához szükségesek...

  public function offsetSet($offset, $value) {
    //$this->container[$offset] = $value;
  }

  public function offsetExists($offset) {
    //return isset($this->container[$offset]);
  }

  public function offsetUnset($offset) {
    //unset($this->container[$offset]);
  }

  public function offsetGet($offset) {
    return isset($this->container[$offset]) ? $this->container[$offset] : null;
  }

  // get metódus...

  public function getValue(){
    return $this->container;
  }

}


A fönti Node osztály konstruktora két dolgot csinál :

1. Először is megvizsgálja, hogy az input egy tömb-e. Ha igen, akkor végigmegy rajta és feltölti az objektum konténerét, mégpedig úgy, hogy mindegyik kulcsnak egy-egy újabb Node objetktumot feletet meg, amik egyenként majd mind-mind újra ellenőrzik (a konstruktoruk által) a kapott értéküket, így rekurzívan felépül a teljes Fa (ami igazából több fa, mert annyiról van szó, ahány gyökérből áll a legkülső tömb) a Node objektumok egymásba ágyazásából.

2. Ha paraméterül nem tömböt kap a Node objektum, akkor szimplán eltárolja az értékét.

Ezután már nincs szükségünk, csak a kezelendő tömbre, és egy foreach ciklusra, ami referencia szerint átveszi a tömb egyes elemeit, és mindegyikből egy Node objektumot hoz létre. A többi munkát már maguk a Node objektumok végzik, a konstruktorukkal a fönt ismertetett módon.

$array = array(
  'elso' => 1,
  'masodik' => 2,
  'hamradik' => 3,
  'negyedik' => array(
    'elso' => 1,
    'masodik' => 2,
    'harmadik' => array(
      'elso' => 1,
      'masodik' => 2,
     )
   )
);

foreach($array as &$value){
  $value = new Node($value);
}


Ha ezzel mind megvagyunk, akkor úgyan úgy kezelhetjük a tömböt mint annak előtte, kivéve persze, hogy immár minden index-et kezelhetünk objektumként is. Például :

// Kiírja hogy 1
echo $array['elso']->getValue();
// Ez meg kiírná, hogy Array, mivel egy újabb Node objektumokból álló tömböt kapnánk vissza
echo $array['negyedik']['masodik']->getValue();
// Kiírja hogy 2
echo $array['negyedik']['harmadik']['masodik']->getValue();


Értelem szerűen, ha a Node osztályban nem kommenteztem volna ki az offsetSet, offsetExists, offsetUnset metódusokat akkor működnének a következő műveletek is :

$array['elso'] = 'balablabla...';
echo isset($array['negyedik']['masodik']) ? 'Létezik' : 'Nem létezik';
unset($array['negyedik']['harmadik']['masodik']);
// Kiírná hogy Nem létezik
echo isset($array['negyedik']['harmadik']['masodik']) ? 'Létezik' : 'Nem létezik';


Sőt, ha a Node osztálynak még __toString metódust is írnánk, akkor még a getValue() metódusra se lenne szükség például kiiratáskor. Ezzel csak az a baj, hogy a Node objektum tárolhat tömböt, és szimpla értéket is, tehát itt azért trükközni kéne a __toString metódussal.

echo $array['negyedik']['masodik'];
5

inf3rno, a te megoldásod jó,

inf · 2010. Szep. 5. (V), 10.16
inf3rno, a te megoldásod jó, de nem a legszebb
Hát annyira nem szeretem a constructor injection-t, aztán azért inkább kitettem staticba. Végülis nem is volt muszáj static-ba tenni, lehetett volna a metódusok közé is a fromArray-t.

A lényeg nem is ez volt, hanem, hogy az "&" használatához azért tapasztalat kell, meg érteni kell, hogy mit csinál az ember.
6

Hát igen...

whiteman0524 · 2010. Szep. 5. (V), 11.08
Sokszor kimegy az ember fejéből, hogy bizonyos ciklusokban meg a függvényekben nem az eredeti adattal dolgozik hanem egy másolattal, ezért referenciázni kell. De annyira azért nem bonyolult a dolog.

A "szép" megoldást, meg természetesen a végeredményre értettem. Lehet hogy másképpen is meg lehetett volna oldani mint így konstruktor által, de a lényeg akkor is az ArrayAccess interfészen van. Azért gondolom abban egyetértünk, hogy a :

$value = $tree['negyedik']['harmadik']['elso']->getValue();

sokkal szebb, mint a :

$value = $tree->get('negyedik')->get('harmadik')->get('elso');


Ha nem értünk egyet, akkor a te megoldásod is ugyan ilyen jó :D Mindenesetre köszi szépen a segítséget. Ez a jó a programozásban, hogy számtalan módon meg lehet oldani egy problémát :)
7

Szívesen segítettem. A

inf · 2010. Szep. 5. (V), 15.11
Szívesen segítettem.

A referenciához még hozzátennék annyit, hogy foreach-nél lehet a tömb elemeket is referenciázni, ahogy csináltam, csak ilyenkor unset-el el kell vágni a referenciát minden ciklus végén.
A hibaüzenet, amit az elején írtál akkor szokott jönni, ha nincs névhez kötve az érték, amit referencia szerint próbálsz átadni. Ilyen akkor fordul elő, amikor return-nek vagy függvényhívásnak értéket adsz közvetlenül, és nem változót. (Az más kérdés, hogy nekem nem jön be, hogy ezért hibát dob a php, de ez van.)

Nem mondtam, hogy a konstruktor rossz megoldás, hiszem működik. :-) Csak annyit, hogy én nem szeretem. Ha konstruktorba teszek ilyesmit, akkor is inkább külön metódusnak adom át onnan is.

ArrayAccess-t régebben én is szerettem, most már nem jön be annyira. A get és put java Map metódusok, és jobban bejönnek. Ha iterálni is akarsz, akkor az IteratorAggregate interface-t is nézd majd meg.
8

Nem is tudtam...

whiteman0524 · 2010. Szep. 5. (V), 19.06
Hogy a foreach-nél ha a ciklusváltozót is referenciaként kezeljük, akkor kell az unset(). És erre egész pontosan mi szükség van ? Most kíváncsi lettem :)
9

Átnéztem kicsit jobban,

inf · 2010. Szep. 5. (V), 22.00
Átnéztem kicsit jobban, egyszerű esetben nem kell. Ha az ember módosítani akarja a referenciaként átadott ciklusváltozót, akkor kell unset-el elvágni a referenciát. Ill a ciklus után a végén, ha ugyanazt a változónevet akarja használni. Szal ez egy rossz berögzült szokás volt. :-) Jó, hogy szóba került, mert úgy látszik én nem értettem még eléggé, köszi. (Elég sokszor adok át referencia szerint dolgokat, mert pl string feldolgozásnál jelentősen tud gyorsítani, viszont ilyenkor kellhet az unset, különben az eredetit módosítja.)

Itt is lehetett volna használni az eredeti tömb felülírására.
10

Értem

whiteman0524 · 2010. Szep. 6. (H), 22.58
Mondjuk a referenciákkal azért nem árt módjával bánni, mert eléggé átláthatatlanná tehetik a kódot. Ez alatt azt értem, hogy például több eljárás (nem függvény) ami referenciákkal dolgozik és ugyanakkor módosít is az adatokon, az eléggé megkavarhatja az összképet ismeretlen szemeknek, vagy akár saját magával is "kitolhat" az ember, ha mondjuk 1-2 év múlva elő kell venni a kódot.

Nekem azt tanították, hogy lehetőleg kerüljük azokat az eljárásokat, amik a függvénytörzsön kívül is "munkálkodnak". Persze nyilván, néha szükség van rájuk, de egyébként ez úgy általánosságban szerintem nem egy rossz tanács, és megfontolandó :)
11

Persze, én sem szívesen

inf · 2010. Szep. 7. (K), 12.38
Persze, én sem szívesen használom őket, de van, ahol muszáj, mert nem a másolt tömböt akarja átadni az ember, hanem az eredetin akar elvégezni valamit. Nagy stringeknél meg szintén jobb, ha nem másolja a php a stringet, mert az jelentősen lassíthat a dolgokon. Sajnos php ilyen, remélem majd egyszer változtatnak ezen.