21

I have a array list (for this example I'm using cell phones). I'm wanting to be able to search for multiple key/value pairs and return it's parent array index.

For example, here is my array:

// $list_of_phones (array)
Array
(
    [0] => Array
        (
            [Manufacturer] => Apple
            [Model] => iPhone 3G 8GB
            [Carrier] => AT&T
        )

    [1] => Array
        (
            [Manufacturer] => Motorola
            [Model] => Droid X2
            [Carrier] => Verizon
        )
)

I'm wanting to be able to do something like the following:

// This is not a real function, just used for example purposes
$phone_id = multi_array_search( array('Manufacturer' => 'Motorola', 'Model' => 'Droid X2'), $list_of_phones );

// $phone_id should return '1', as this is the index of the result.

Any ideas or suggestions on how I can or should do this?

FrankS101
  • 2,112
  • 6
  • 26
  • 40
Wes Foster
  • 8,770
  • 5
  • 42
  • 62

7 Answers7

27

Perhaps this will be useful:

  /**
   * Multi-array search
   *
   * @param array $array
   * @param array $search
   * @return array
   */
  function multi_array_search($array, $search)
  {

    // Create the result array
    $result = array();

    // Iterate over each array element
    foreach ($array as $key => $value)
    {

      // Iterate over each search condition
      foreach ($search as $k => $v)
      {

        // If the array element does not meet the search condition then continue to the next element
        if (!isset($value[$k]) || $value[$k] != $v)
        {
          continue 2;
        }

      }

      // Add the array element's key to the result array
      $result[] = $key;

    }

    // Return the result array
    return $result;

  }

  // Output the result
  print_r(multi_array_search($list_of_phones, array()));

  // Array ( [0] => 0 [1] => 1 )

  // Output the result
  print_r(multi_array_search($list_of_phones, array('Manufacturer' => 'Apple')));

  // Array ( [0] => 0 )

  // Output the result
  print_r(multi_array_search($list_of_phones, array('Manufacturer' => 'Apple', 'Model' => 'iPhone 6')));

  // Array ( )

As the output shows, this function will return an array of all keys with elements which meet all the search criteria.

Michael
  • 11,912
  • 6
  • 49
  • 64
  • Nice function, it works as described. The second `foreach` basically does what `array_search` does in my rigged bit of code above. Since you're the only real answer so far, and it does work, I'll mark your answer as the accepted one. Thanks for the feedback! – Wes Foster Dec 19 '12 at 02:13
  • 1
    `array_search` isn't exactly the same. It doesn't check the keys, only the values, so it doesn't check to see if the `Manufacturer` is `Apple`, only if `Apple` is in the array somewhere (they might become a carrier at some point). – Michael Dec 19 '12 at 09:13
  • 2
    This solution should be accepted. Just one problem: if some arrays contain `NULL` (for ex.: `['Manufacturer' => 'Motorola', 'Model' => NULL]`), condition `if (!isset($value[$k]) || $value[$k] != $v)` won't trigger. So, I propose to change that condition: `if ((!isset($value[$k]) && $value[$k] !== null) || $value[$k] != $v)`. – Boolean_Type Nov 15 '17 at 16:18
  • I never new before now that there was an optional numeric argument for `continue`. Nice! – John Rix May 18 '19 at 23:25
6

I ended up doing the following. It's not pretty, but works very well. For anyone reading, feel free to update with a DRYer answer:

// Variables for this example
$carrier = 'Verizon';
$model = 'Droid X2';
$manufacturer = 'Motorola';

// The foreach loop goes through each key/value of $list_of_phones and checks
// if the given value is found in that particular array. If it is, it then checks
// a second parameter (model), and so on.
foreach ($list_of_phones as $key => $object)
{
    if ( array_search($carrier, $object) )
    {
        if ( array_search($model, $object) )
        {
            if ( array_search($manufacturer, $object) )
            {
                // Return the phone from the $list_of_phones array
                $phone = $list_of_phones[$key];
            }
        }
    }
}

Works like a charm.

Wes Foster
  • 8,770
  • 5
  • 42
  • 62
  • 4
    To my surprise, this method is actually 5 times faster than the accepted answer. Granted it's a small array, but the difference is significant. Here's a fiddle. http://www.tehplayground.com/#du9XzNFqs – MikelG Nov 10 '15 at 21:52
  • 1
    I just used this method to compare elements from 2 multidimensional arrays, to define if an item from one array has "received" status. it worked like a charm. – Alfonso Oct 18 '18 at 14:04
5

you may use array_intersect_key and array_intersect and array_search

check array_intersect_key php manual to get array of items with matching keys

and array_intesect php manual to get array if items with matching values

u can get value of key in array using $array[key]

and get key of value in array using array_search $key = array_search('green', $array);

php.net/manual/en/function.array-search.php

Khalil Awada
  • 111
  • 8
  • I ended up looping through `$list_of_phones` with a foreach loop, then array_searching each result for the values. It works, but still looks hacked up. If you can explain your method a little more in depth, I'll give it a try. – Wes Foster Dec 17 '12 at 23:38
3

This way works for a multidimensinal array like yours:

$test = array_intersect_key($list_of_phones, array(array("Manufacturer" => "Motorola", "Carrier" => "Verizon")));
Tunaki
  • 132,869
  • 46
  • 340
  • 423
2

I expanded @MichaelRushton's code by adding support for different comparison operators:

function multi_array_search ($array, $search) {
    $result = [];

    foreach ($array as $key => $value) { //iterate over each array element
        foreach ($search as $k => $v) { //iterate over each search condition
            $operator = $v[0];
            $searchField = $v[1];
            $searchVal = $v[2];

            switch ($operator) {
                case '=':
                    $cond = ($value[$searchField] != $searchVal);
                    break;

                case '!=':
                    $cond = ($value[$searchField] == $searchVal);
                    break;

                case '>':
                    $cond = ($value[$searchField] <= $searchVal);
                    break;

                case '<':
                    $cond = ($value[$searchField] >= $searchVal);
                    break;

                case '>=':
                    $cond = ($value[$searchField] < $searchVal);
                    break;

                case '<=':
                    $cond = ($value[$searchField] > $searchVal);
                    break;
            }

            //if the array element does not meet the search condition then continue to the next element
            if ((!isset($value[$searchField]) && $value[$searchField] !== null) || $cond) {
                continue 2;
            }
        }
        $result[] = $key; //add the array element's key to the result array
    }
    return $result;
}

    //incoming data:
    $phonesList = [
        0 => [
            'Manufacturer' => 'Apple',
            'Model' => 'iPhone 3G 8GB',
            'Carrier' => 'AT&T',
            'Cost' => 100000
        ],
        1 => [
            'Manufacturer' => 'Motorola',
            'Model' => 'Droid X2',
            'Carrier' => 'Verizon',
            'Cost' => 120000
        ],
        2 => [
            'Manufacturer' => 'Motorola',
            'Model' => 'Droid X2',
            'Carrier' => 'Verizon',
            'Cost' => 150000
        ]
    ];

    var_dump(multi_array_search($phonesList, 
                             [ ['=', 'Manufacturer', 'Motorola'], 
                               ['>', 'Cost', '130000'] ]
            ));

   //output:
   array(1) { [0]=> int(2) }
Boolean_Type
  • 1,146
  • 3
  • 13
  • 40
2

This is the same as @Boolean_Type but enhanced a bit to simplify things.

function multi_array_search($array, $search)
{
    $result = array();

    foreach ($array as $key => $val)
    {
        foreach ($search as $k => $v)
        {
            // We check if the $k has an operator.
            $operator = '=';
            if (preg_match('(<|<=|>|>=|!=|=)', $k, $m) === 1)
            {
                // We change the operator.
                $operator = $m[0];

                // We trim $k to remove white spaces before and after.
                $k = trim(str_replace($m[0], '', $k));
            }

            switch ($operator)
            {
                case '=':
                    $cond = ($val[$k] != $v);
                    break;

                case '!=':
                    $cond = ($val[$k] == $v);
                    break;

                case '>':
                    $cond = ($val[$k] <= $v);
                    break;

                case '<':
                    $cond = ($val[$k] >= $v);
                    break;

                case '>=':
                    $cond = ($val[$k] < $sv);
                    break;

                case '<=':
                    $cond = ($val[$k] > $sv);
                    break;
            }

            if (( ! isset($val[$k]) && $val[$k] !== null) OR $cond)
            {
                continue 2;
            }
        }

        $result[] = $key;
    }

    return $result;
}  

This way, you can simply search like this:

$keys = multi_array_search($phonesList, array(
    'Manufacturer' => 'Motorola',
    'Cost >'       => '130000',
));   

If found, you will have and array of indices like so: array(1, 25, 33) (This is only an example).

Kader Bouyakoub
  • 398
  • 2
  • 11
1
// $needle example: ["Manufacturer" => "Motorola", "Carrier" => "Verizon"]
$needle = ['1st-key' => $value1, '2nd-key' => $value2];
$result = array_filter($haystack, function($item) use ($needle) {
  return ($item['1stkey'] == $needle['1st-key'] & $item['2nd-key'] == $needle['2nd-key']);
});

I'm using something like this and it works. It returns an array keyed by the right keys of the corresponding $haystack items. I think this is much more meaningful and compact.