Wielowymiarowe wyszukiwanie w tablicy PHP (znajdź klucz według określonej wartości)

114

Mam tę wielowymiarową tablicę. Muszę go przeszukać i zwrócić tylko klucz pasujący do wartości „slug”. Wiem, że istnieją inne wątki dotyczące wyszukiwania wielowymiarowych tablic, ale nie jestem wystarczająco zrozumiały, aby zastosować się do mojej sytuacji. Bardzo dziękuję za pomoc!

Więc potrzebuję funkcji takiej jak:

myfunction($products,'breville-one-touch-tea-maker-BTM800XL');
// returns 1

Oto tablica:

$products = array (
1  => array(
        'name'          => 'The Breville One-Touch Tea Maker',
        'slug'          => 'breville-one-touch-tea-maker-BTM800XL',
        'shortname'     => 'The One-Touch Tea Maker',
        'listprice'     => '299.99',
        'price'         => '249.99',
        'rating'        => '9.5',
        'reviews'       => '81',
        'buyurl'        => 'http://www.amazon.com/The-Breville-One-Touch-Tea-Maker/dp/B003LNOPSG',
        'videoref1'     => 'xNb-FOTJY1c',
        'videoref2'     => 'WAyk-O2B6F8',
        'image'         => '812BpgHhjBML.jpg',
        'related1'      => '2',
        'related2'      => '3',
        'related3'      => '4',
        'bestbuy'       => '1',
        'quote'         => '',
        'quoteautor'    => 'K. Martino',
        ),

2  => array(
        'name'          => 'Breville Variable-Temperature Kettle BKE820XL',
        'slug'          => 'breville-variable-temperature-kettle-BKE820XL',
        'shortname'     => 'Variable Temperature Kettle',
        'listprice'     => '199.99',
        'price'         => '129.99',
        'rating'        => '9',
        'reviews'       => '78',
        'buyurl'        => 'http://www.amazon.com/Breville-BKE820XL-Variable-Temperature-1-8-Liter-Kettle/dp/B001DYERBK',
        'videoref1'     => 'oyZWBD83xeE',
        'image'         => '41y2B8jSKmwL.jpg',
        'related1'      => '3',
        'related2'      => '4',
        'related3'      => '5',
        'bestbuy'       => '1',
        'quote'         => '',
        'quoteautor'    => '',
        ),
);
Ben Kouba
źródło

Odpowiedzi:

157

Bardzo prosty:

function myfunction($products, $field, $value)
{
   foreach($products as $key => $product)
   {
      if ( $product[$field] === $value )
         return $key;
   }
   return false;
}
Aurelio De Rosa
źródło
6
Jeśli używasz tej funkcji w instrukcji warunkowej, będziesz chciał bezwzględnie sprawdzić typ, ponieważ zwracany klucz może czasami mieć indeks [0]. Więc jeśli przeprowadzasz kontrolę warunkową, powinno to wyglądać if (myfunction($array, 'field', 'value') !== FALSE )) // do something...
Andy Cook
159

Innym możliwym rozwiązaniem jest array_search()funkcja. Musisz używać PHP 5.5.0 lub nowszego.

Przykład

$userdb=Array
(
(0) => Array
    (
        (uid) => '100',
        (name) => 'Sandra Shush',
        (url) => 'urlof100'
    ),

(1) => Array
    (
        (uid) => '5465',
        (name) => 'Stefanie Mcmohn',
        (pic_square) => 'urlof100'
    ),

(2) => Array
    (
        (uid) => '40489',
        (name) => 'Michael',
        (pic_square) => 'urlof40489'
    )
);

$key = array_search(40489, array_column($userdb, 'uid'));

echo ("The key is: ".$key);
//This will output- The key is: 2

Wyjaśnienie

Funkcja array_search()ma dwa argumenty. Pierwsza to wartość, którą chcesz przeszukać. Drugi to miejsce, w którym funkcja powinna szukać. Funkcja array_column()pobiera wartości elementów, których jest klucz 'uid'.

Podsumowanie

Możesz więc użyć go jako:

array_search('breville-one-touch-tea-maker-BTM800XL', array_column($products, 'slug'));

lub jeśli wolisz:

// define function
function array_search_multidim($array, $column, $key){
    return (array_search($key, array_column($array, $column)));
}

// use it
array_search_multidim($products, 'slug', 'breville-one-touch-tea-maker-BTM800XL');

Oryginalny przykład (autorstwa xfoxawy) można znaleźć na DOCS . Strona .
array_column()


Aktualizacja

Ze względu na komentarz Vael byłem zaciekawiony, więc wykonałem prosty test, aby sprawdzić działanie metody, której używa, array_searchi metody zaproponowanej na zaakceptowanej odpowiedzi.

Stworzyłem tablicę zawierającą 1000 tablic, struktura była następująca (wszystkie dane były losowe):

[
      {
            "_id": "57fe684fb22a07039b3f196c",
            "index": 0,
            "guid": "98dd3515-3f1e-4b89-8bb9-103b0d67e613",
            "isActive": true,
            "balance": "$2,372.04",
            "picture": "http://placehold.it/32x32",
            "age": 21,
            "eyeColor": "blue",
            "name": "Green",
            "company": "MIXERS"
      },...
]

Przeprowadziłem test wyszukiwania 100 razy, szukając różnych wartości dla pola nazwy, a następnie obliczyłem średni czas w milisekundach . Tutaj możesz zobaczyć przykład.

Wyniki były takie, że metoda zaproponowana w tej odpowiedzi wymagała około 2E-7, aby znaleźć wartość, podczas gdy zaakceptowana metoda odpowiedzi wymagała około 8E-7.

Jak powiedziałem wcześniej, oba czasy są całkiem do przyjęcia dla aplikacji używającej tablicy o tym rozmiarze. Jeśli rozmiar znacznie wzrośnie, powiedzmy 1 mln elementów, to ta niewielka różnica również się zwiększy.

Aktualizacja II

Dodałem test dla metody, na podstawie array_walk_recursivektórej wymieniono niektóre odpowiedzi tutaj. Otrzymany wynik jest prawidłowy. A jeśli skupimy się na wydajności, jest nieco gorsza niż w przypadku innych badanych w teście . W teście widać, że jest to około 10 razy wolniejsze niż metoda oparta na array_search. Ponownie, nie jest to bardzo istotna różnica dla większości aplikacji.

Aktualizacja III

Podziękowania dla @mickmackusa za dostrzeżenie kilku ograniczeń tej metody:

  • Ta metoda zakończy się niepowodzeniem w przypadku kluczy asocjacyjnych.
  • Ta metoda będzie działać tylko na podtablicach indeksowanych (zaczynając od 0 i posiadających kolejno rosnąco klucze).
Iván Rodríguez Torres
źródło
Czy ktoś zna wydajność tego? Wygląda na to, że ostatecznie byłby wolniejszy i nadal wymagałby 5.5. Nie mogę przetestować, ponieważ jestem na 5.4.
Vael Victus
Dla każdego, kto nie rozumie: w PHP 7 pętle for są szybsze. Kiedy zmieniłem na 5.6 w tym przykładzie eval.in, array_search był nieco szybszy.
Vael Victus
sprytny! Robiłem coś podobnego, używając array_combine () z array_column (), aby utworzyć kolejną tablicę, z której można pobrać moje dane ze znanym kluczem, ale jest to bardziej eleganckie.
David
4
Używanie array_search()with array_column()nie zadziała na przykładowej tablicy OP, ponieważ klucze podtablicy zaczynają się od 1. Ta metoda nie powiedzie się również w przypadku kluczy asocjacyjnych. Ta metoda będzie działać tylko na indeksowanych podtablicach (zaczynając od 0i posiadających kolejno rosnące klucze). Powodem tego jest to, że array_column()wygeneruje nowe indeksy w zwróconej tablicy.
mickmackusa
całkowicie słusznie @mickmackusa, dodałem Twoją wiedzę do odpowiedzi. Dzięki za pomoc
Iván Rodríguez Torres
14

Ta metoda klasy może wyszukiwać w tablicy według wielu warunków:

class Stdlib_Array
{
    public static function multiSearch(array $array, array $pairs)
    {
        $found = array();
        foreach ($array as $aKey => $aVal) {
            $coincidences = 0;
            foreach ($pairs as $pKey => $pVal) {
                if (array_key_exists($pKey, $aVal) && $aVal[$pKey] == $pVal) {
                    $coincidences++;
                }
            }
            if ($coincidences == count($pairs)) {
                $found[$aKey] = $aVal;
            }
        }

        return $found;
    }    
}

// Example:

$data = array(
    array('foo' => 'test4', 'bar' => 'baz'),
    array('foo' => 'test',  'bar' => 'baz'),
    array('foo' => 'test1', 'bar' => 'baz3'),
    array('foo' => 'test',  'bar' => 'baz'),
    array('foo' => 'test',  'bar' => 'baz4'),
    array('foo' => 'test4', 'bar' => 'baz1'),
    array('foo' => 'test',  'bar' => 'baz1'),
    array('foo' => 'test3', 'bar' => 'baz2'),
    array('foo' => 'test',  'bar' => 'baz'),
    array('foo' => 'test',  'bar' => 'baz'),
    array('foo' => 'test4', 'bar' => 'baz1')
);

$result = Stdlib_Array::multiSearch($data, array('foo' => 'test4', 'bar' => 'baz1'));

var_dump($result);

Będzie produkować:

array(2) {
  [5]=>
  array(2) {
    ["foo"]=>
    string(5) "test4"
    ["bar"]=>
    string(4) "baz1"
  }
  [10]=>
  array(2) {
    ["foo"]=>
    string(5) "test4"
    ["bar"]=>
    string(4) "baz1"
  }
}
Fatalista
źródło
Cześć Fatalist stackoverflow.com/questions/40860030/… . Odnosi się do tego pytania, czy możesz wyjaśnić to pytanie
KARTHI SRV
4

Użyj tej funkcji:

function searchThroughArray($search,array $lists){
        try{
            foreach ($lists as $key => $value) {
                if(is_array($value)){
                    array_walk_recursive($value, function($v, $k) use($search ,$key,$value,&$val){
                        if(strpos($v, $search) !== false )  $val[$key]=$value;
                    });
            }else{
                    if(strpos($value, $search) !== false )  $val[$key]=$value;
                }

            }
            return $val;

        }catch (Exception $e) {
            return false;
        }
    }

i wywołać funkcję.

print_r(searchThroughArray('breville-one-touch-tea-maker-BTM800XL',$products));
josef
źródło
Niezła odpowiedź. Możesz sprawdzić wykonanie swojej propozycji na mojej odpowiedzi
Iván Rodríguez Torres
Odpowiedzi zawierające tylko kod mają niską wartość w StackOverflow. Zaktualizuj swój post, aby wyjaśnić, jak działa funkcja wyszukiwania podciągów węzłów liścia. Ta metoda nie została zaprojektowana specjalnie do pracy, o którą prosi PO, dlatego ważne jest, aby wyjaśnić różnice. Link do wersji demonstracyjnej znacznie poprawiłby zrozumienie czytelnika. Zawsze publikuj odpowiedzi z zamiarem poinformowania OP i szerszej publiczności.
mickmackusa
1
function search($array, $key, $value) 
{ 
    $results = array(); 

    if (is_array($array)) 
    { 
        if (isset($array[$key]) && $array[$key] == $value) 
            $results[] = $array; 

        foreach ($array as $subarray) 
            $results = array_merge($results, search($subarray, $key, $value)); 
    } 

    return $results; 
} 
mikelee
źródło
Odpowiedzi zawierające tylko kod mają niską wartość w StackOverflow. Zaktualizuj swój post, aby wyjaśnić, jak działa metoda rekurencyjna, sytuacje, w których jest to odpowiednie, i sytuacje, w których rekursja jest niepotrzebnym narzutem. Zawsze publikuj odpowiedzi z zamiarem poinformowania OP i szerszej publiczności.
mickmackusa
1

Dla następnego odwiedzającego: użyj rekursywnego spaceru po tablicy; odwiedza każdy „liść” w wielowymiarowej tablicy. Oto inspiracja:

function getMDArrayValueByKey($a, $k) {
    $r = [];
    array_walk_recursive ($a, 
                          function ($item, $key) use ($k, &$r) {if ($key == $k) $r[] = $item;}
                          );
    return $r;
}
Hans
źródło
Nie ma problemu! aby zaoszczędzić czas, jeśli spróbujesz odpowiedzieć josef, funkcja zwraca tablicę z jednym elementem. Kluczem jest pożądana odpowiedź :)
Iván Rodríguez Torres
Odpowiedź @Ivan josef bardzo różni się od tej. Czy sam to przetestowałeś. Ciągle wpatruję się w tę odpowiedź i nie sądzę, żeby to zadziałało, ponieważ array_walk_recursive nie widzi wyższego poziomu. Dla każdego klucza pierwszego poziomu josef wywołuje strpos lub sprawdza wszystkie leafnodes. Zobacz różnicę?
mickmackusa
Oczywiście @mickmackusa Ale Hans daje jakąś inspirację, odpowiedzią nie jest dosłowne podanie rozwiązania. Wymaga to bardziej rozwinięcia, tak jak Josef w swojej odpowiedzi. Ale masz rację, że ta odpowiedź nie rozwiązuje całkowicie problemu.
Iván Rodríguez Torres
1

Chciałbym zrobić to poniżej, gdzie $productsjest rzeczywista tablica podana w problemie na samym początku.

print_r(
  array_search("breville-variable-temperature-kettle-BKE820XL", 
  array_map(function($product){return $product["slug"];},$products))
);
Sam Kaz
źródło
0

Spróbuj tego

function recursive_array_search($needle,$haystack) {
        foreach($haystack as $key=>$value) {
            $current_key=$key;
            if($needle==$value['uid'] OR (is_array($value) && recursive_array_search($needle,$value) !== false)) {
                return $current_key;
            }
        }
        return false;
    }
pawan sen
źródło
Odpowiedzi zawierające tylko kod mają niską wartość w StackOverflow. Zaktualizuj swój post, aby wyjaśnić, jak działa metoda rekurencyjna, sytuacje, w których jest to odpowiednie, i sytuacje, w których rekursja jest niepotrzebnym narzutem. Zawsze publikuj odpowiedzi z zamiarem poinformowania OP i szerszej publiczności. Ps Myślę, że większość programistów php będzie wolała &&i ||zamiast ANDiOR twoim stanie. Nie ma powodu do deklaracji current_key. Porównanie $needlepowinno być ścisłe.
mickmackusa