Käytössä on PHP. Minulla on moniulotteinen taulukko.
Array ( [183] => Array ( [info] => Array ( [name] => Ben [points] => 4800 ) ) [380] => Array ( [info] => Array ( [name] => Ruben [points] => 14500 ) ) [450] => Array ( [info] => Array ( [name] => Alex [points] => 4800 ) ) )
PHP-koodi järjestää/lajittelee taulukon ja se toimii ihan oikein.
Ensimmäinen lajittelukriteeri on points, jonka mukaan lajitellaan laskevasti. Toinen kriteeri on name, lajittelu nousevasti.
uasort($array, function($a, $b) { if ($a['info']['points'] == $b['info']['points']) { return strcmp($a['info']['name'], $b['info']['name']); } return $b['info']['points'] - $a['info']['points']; });
Kun PHP-koodi lajittelee taulukon, syntyvä taulukko on tällainen:
Array ( [380] => Array ( [info] => Array ( [name] => Ruben [points] => 14500 ) ) [450] => Array ( [info] => Array ( [name] => Alex [points] => 4800 ) ) [183] => Array ( [info] => Array ( [name] => Ben [points] => 4800 ) ) )
Muuten homma toimii hienosti, mutta haluaisin saada lisättty myös position-arvot mieluummin lajittelun lomassa tai jos se ei onnistu, niin sitten vasta lajittelun jälkeen vaikkapa foreach-silmukassa.
Mitä enemmän pisteitä (points), sitä pienempi position. Jos points on sama, on myös position-arvojen oltavat samat.
Lopullinen taulukko pitäisi saada tällaiseksi:
Array ( [380] => Array ( [info] => Array ( [name] => Ruben [points] => 14500 [position] => 1 ) ) [450] => Array ( [info] => Array ( [name] => Alex [points] => 4800 [position] => 2 /* koska Alexilla points on pienempi kuin Rubenilla */ ) ) [183] => Array ( [info] => Array ( [name] => Ben [points] => 4800 [position] => 2 /* koska Benillä points on sama kuin Alexilla */ ) ) )
Mitä homma hoituisi mahdollisimman nopeasti foreach-silmukalla tai jollakin muulla tavalla?
$position = 0; $position_for_points = 0; $points = null; foreach ($taulukko as $i => $_) { $position += 1; if ($taulukko[$i]["info"]["points"] !== $points) { $position_for_points = $position; $points = $taulukko[$i]["info"]["points"]; } $taulukko[$i]["info"]["position"] = $position_for_points; }
Sivuhuomio: Joku ehkä ihmettelee, miksi en käytä koodissa viittauksia (&$rivi). Syy on se, että viittauksilla tulee helposti virheitä, koska silmukan jälkeen viittaus jää viimeiseen riviin ja samannimisen muuttujan käyttäminen myöhemmin ilman viittausta muuttaakin odottamatta sitä taulukon viimeistä riviä.
Kiitos. Miksi !== eikä != tuossa? Eikö muuttujan $points arvoa päivitetä välillä? En nyt pysty tätä testaamaan, serveri alhaalla...
xxmss kirjoitti:
Miksi !== eikä != tuossa?
Jotta koodi toimisi myös tilanteessa, jossa kärjellä on nolla pistettä. 0 == null mutta 0 !== null.
xxmss kirjoitti:
Eikö muuttujan $points arvoa päivitetä välillä?
Päivitetään. Poistin sen rivin vahingossa, kun muokkasin koodia. Nyt koodi on taas oikein edellisessä viestissä.
Kiitos lisäinfosta. Voisiko alussa olla $points = 0; ja sitten if-lauseessa != $points?
Oliomallinen ratkaisu iteraattoria käyttäen. Tällöin rankkaus tehdään lennosta taulukkoa loopatessa ja välttyy ylimääräiseltä silmukalta.
<?php class RankIterator extends ArrayIterator { /** * Iterator index 0...n */ protected $i; /** * Ranking */ protected $pos; /** * Function used to compare two values. */ protected $comparison; /** * Key to use for storing item ranking. */ protected $rank_key; /** * Value from last iteration to be used for comparison. */ protected $last; public function __construct(array $data, $rank_key, callable $comparison) { parent::__construct($data); $this->rank_key = $rank_key; $this->comparison = $comparison; } public function current() { $item = parent::current(); if ($this->i > 0) { $delta = call_user_func($this->comparison, $this->last, $item); if ($delta > 0) { $this->pos = $this->i + 1; } elseif ($delta < 0) { throw new Error('Source data is not sorted'); } } $item[$this->rank_key] = $this->pos; $this->last = $item; return $item; } public function next() { parent::next(); $this->i++; } public function rewind() { parent::rewind(); $this->i = 0; $this->pos = 1; $this->last = null; } } $data = [ ['points' => 100, 'name' => 'Miska'], ['points' => 95, 'name' => 'Laura'], ['points' => 87, 'name' => 'Siiri'], ['points' => 87, 'name' => 'Jaakko'], ['points' => 45, 'name' => 'Petteri'], ]; $iterator = new RankIterator($data, 'rank', function($a, $b) { return $a['points'] - $b['points']; }); print_r(iterator_to_array($iterator));
Aihe on jo aika vanha, joten et voi enää vastata siihen.