Kirjautuminen

Haku

Tehtävät

Keskustelu: Nettisivujen teko: PHP: Rivin etsiminen moniulotteisesta taulukosta

AkeMake [02.11.2016 17:36:36]

#

Löysin array_search dokumentaatiosta kommentin, jolla pitäisi saada useampi ulotteisesta taulukosta haluttu avain ulos. Arvelin, ettei se päde ihan joka tilanteessa ja foorumeita luettuani ja itse testattuani sain vahvistuksen epäilyyn.

$user = array(1 => array('etunimi' => 'Matti', 'sukunimi' => 'Meikäläinen'),
              3 => array('etunimi' => 'Maija', 'sukunimi' => 'Meikäläinen'));

$key = array_search('Maija', array_column($user, 'etunimi'));
echo var_export($user[$key], true);

/*
 * array_column palauttaa array(0 => 'Matti', 1 => 'Maija'),
 * jolloin $key on 1 vaikka pitäisi olla 3
 * eli ulos tulee Matin taulukko eikä haluttu Maija.
 */

Toki tämän saisi ratkaistua käymällä foreach silmukassa jokainen taulukko läpi ja sitä rataa, mutta olisiko tähän olemassa jotain simppeliä lyhyttä yhden rivin vastausta niin kuin tämä esimerkki, joka ei lopulta toiminutkaan joka tilanteessa? Itse pohdiskelin jotenkin array_map funktion käyttöä, mutta en saanut kiinni ajatuksesta miten se toteutetaan.

Metabolix [02.11.2016 19:48:52]

#

Koska array_column laittaa indeksoinnin alkamaan nollasta, täytyy hakea data alkuperäisestä taulukosta järjestysnumeron eikä avaimen perusteella. Tähän käy array_slice:

$user = [
  1 => ['etunimi' => 'Matti', 'sukunimi' => 'Meikäläinen'],
  3 => ['etunimi' => 'Maija', 'sukunimi' => 'Meikäläinen']
];

$i = array_search('Maija', array_column($user, 'etunimi'));
if ($i === false) {
  throw new Exception("Not found!");
}
$tulos = array_slice($user, $i, 1)[0];

echo var_export($tulos, true);

Tämä ”näppärä” ratkaisu ei ole välttämättä kovin tehokas, koska array_column luo kopion kaikesta sarakkeen datasta, mikä on ehkä hitaampaa kuin vain taulukon käsittely silmukassa.

On varmasti montakin viritelmää, jolla haluamasi tulos saavutetaan, mutta ei ole tietääkseni hyvää yhden rivin tapaa. Selvintä ja luultavasti tehokkaintakin on tehdä funktio, jossa on silmukka. Sen jälkeen itse kutsu sopii taas yhdelle riville. ;)

function array_search_column($array, $column, $value) {
  foreach ($array as $key => $row)
    if ($row[$column] === $value)
      return $key;
  return false;
};
$key = array_search_column($user, "etunimi", "Maija");
if ($key === false) {
  throw new Exception("Not found!");
}
$tulos = $user[$key];

Jos samanlaisia hakuja tehdään paljon, voi olla järkevää järjestellä data ylipäänsä jotenkin toisin.

The Alchemist [03.11.2016 11:04:08]

#

Mikäli joudut tekemään tuollaisia hakuja monta kertaa peräkkäin, niin voisi olla parempi luoda hakupuita valmiiksi, jolloin voit yksinkertaisesti lukea tulokset taulukosta käyttäen avaimena annettua arvoa (nimeä).

Huom. Alla oleva koodi käyttää parissa kohti operaattoria ??, joka vaatii php 7:n.

<?php

class ArrayStore implements ArrayAccess, IteratorAggregate
{
    private $data;
    private $rules;
    private $cache;

    public function __construct(array $rules, array $data = [])
    {
        $this->data = [];
        $this->rules = $rules;
        $this->cache = array_fill_keys($rules, []);

        foreach ($data as $id => $values) {
            $this->add($id, $values);
        }
    }

    public function getIterator()
    {
        return new ArrayIterator($this->data);
    }

    public function offsetExists($key)
    {
        return array_key_exists($key, $this->data);
    }

    public function offsetGet($key)
    {
        return $this->data[$key] ?? null;
    }

    public function offsetSet($id, $value)
    {
        $this->add($id, $value);
    }

    public function offsetUnset($key)
    {
        unset($this->data[$key]);
    }

    public function findBy($key, $value)
    {
        if (!isset($this->cache[$key])) {
            throw new Exception(sprintf('Key \'%s\' is not indexed', $key));
        }

        $cache_id = strtolower($value);
        $ids =  $this->cache[$key][$cache_id] ?? [];

        return array_intersect_key($this->data, array_flip($ids));
    }

    public function add($id, array $values)
    {
        while (is_null($id) || $this[$id]) {
            $id = rand(1, 999999);
        }

        $this->data[$id] = $values;

        foreach ($this->rules as $key) {
            $cache_id = strtolower($values[$key]);
            $this->cache[$key][$cache_id][] = $id;
        }
    }
}
$data = new ArrayStore(['first_name', 'last_name']);

$data[1] = [
    'first_name' => 'Siiri',
    'last_name' => 'Latvala',
];

$data[7] = [
    'first_name' => 'Liisa',
    'last_name' => 'Sirkkanen',
];

$data[132] = [
    'first_name' => 'Jaakko',
    'last_name' => 'Kivelä',
];

$data[] = [
    'first_name' => 'Ville',
    'last_name' => 'Latvala',
];

$data[] = [
    'first_name' => 'Liisa',
    'last_name' => 'Kekkonen',
];

print_r($data->findBy('first_name', 'liisa'));

Vastaus

Aihe on jo aika vanha, joten et voi enää vastata siihen.

Tietoa sivustosta