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?

0

7 Answers 7

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.

Sign up to request clarification or add additional context in comments.

4 Comments

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!
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).
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).
I never new before now that there was an optional numeric argument for continue. Nice!
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.

2 Comments

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. tehplayground.com/#du9XzNFqs
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.
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

1 Comment

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.
3

This way works for a multidimensinal array like yours:

$test = array_intersect_key($list_of_phones, array(array("Manufacturer" => "Motorola", "Carrier" => "Verizon")));

2 Comments

Just test php version 7.4. Its working well
I am sorry but this is not working.
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) }

Comments

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).

Comments

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.