1

I need to do fast lookups to find if an array exists in an array. If I knew the depth of the array It would be easy - and fast!

$heystack['lev1']['lev2']['lev3'] = 10; // $heystack stores 10,000s of arrays like this

if(isset($heystack[$var1][$var2][$var3])) do something...

How would you do this dynamically if you don't know the depth? looping and searching at each level will be too slow for my application.

animuson
  • 53,861
  • 28
  • 137
  • 147
user1170043
  • 11
  • 1
  • 2
  • You want to check if `$heystack[$var1][$var2][$var3]` is an array? Like using `is_array($heystack[$var1][$var2][$var3])`? – animuson Jan 25 '12 at 20:53
  • If you don't know the depth/position of elements you might try [`RecursiveArrayIterator`](http://php.net/manual/en/class.recursivearrayiterator.php) –  Jan 25 '12 at 20:53
  • by "not knowing the depth" do you mean you don't know the position of $var1 2 and 3 ? – Stewie Jan 25 '12 at 20:53
  • Maybe for you `in_array()` (allows search array in array) will help somehow... And about the depth... are the all of the arrays in array same depth? – devdRew Jan 25 '12 at 20:56

2 Answers2

2

Your question has already the answer:

if (isset($heystack[$var1][$var2][$var3]))
{
   # do something...
}

If you don't know the how many $var1 ... $varN you have, you can only do it dynamically which involves either looping or eval and depends if you need to deal with string or numerical keys. This has been already asked and answered:

If you are concerned about speed, e.g. if the array is always the same but you need to query it often, create a index first that has compound keys so you can more easily query it. That could be either done by storing all keys while traversing the array recursively:

class CompoundKeys extends RecursiveIteratorIterator
{
    private $keys;
    private $separator;
    public function __construct($separator, RecursiveIterator $iterator, $mode = RecursiveIteratorIterator::SELF_FIRST, $flags = 0)
    {
        $this->separator = $separator;
        parent::__construct($iterator, $mode, $flags);
    }
    public function current()
    {
        $current = parent::current();
        if (is_array($current))
        {
            $current = array_keys($current);
        }
        return $current;
    }
    public function key()
    {
        $depth = $this->getDepth();
        $this->keys[$depth] = parent::key();
        return implode('.', array_slice($this->keys, 0, $depth+1));
    }
}

Usage:

$it = new CompoundKeys('.', new RecursiveArrayIterator($array));
$compound = iterator_to_array($it, 1);
isset($compound["$var1.$var2.$var3"]);

Alternatively this can be done by traversing recursively and referencing the original arrays values:

/**
 * create an array of compound array keys aliasing the non-array values
 * of the original array.
 *
 * @param string $separator
 * @param array $array
 * @return array
 */
function array_compound_key_alias(array &$array, $separator = '.')
{
    $index = array();
    foreach($array as $key => &$value)
    {
        if (is_string($key) && FALSE !== strpos($key, $separator))
        {
            throw new InvalidArgumentException(sprintf('Array contains key ("%s") with separator ("%s").', $key, $separator));
        }
        if (is_array($value))
        {
            $subindex = array_compound_key_alias($value, $separator);
            foreach($subindex as $subkey => &$subvalue)
            {
                $index[$key.$separator.$subkey] = &$subvalue;
            }
        }
        else
        {
            $index[$key] = &$value;
        }
    }
    return $index;
}

Usage:

$index = array_compound_key_alias($array);
isset($index["$var1.$var2.$var3"]);
Community
  • 1
  • 1
hakre
  • 193,403
  • 52
  • 435
  • 836
  • I just want to mention that isset($heystack[$var1][$var2][$var3] will fail for a given array which has the keys $var1 and $var2 but not $var3. Instead $var3 will converted to a string and this will return the first char of the value in $var2. So isset will return true but $key3 doesn't exist! // For knowing the depth and the keys if(is_array($heystack[$var1][$var2]) && isset($heystack[$var1][$var2][$var3])) ... – uriel Jul 26 '12 at 15:34
  • Actually `isset` will return also `false` when the element exists but is set to `null`. That are common caveats. You showed a solution for the problem that can happen for the wrong implementation of [substring access](http://php.net/manual/en/language.types.string.php), for the isset problem it's [`array_key_exists`](http://de.php.net/array_key_exists). – hakre Jul 26 '12 at 15:50
0

You'll need some sort of looping but you won't need to traverse the entire depth. You can simply use a function that does the equivalent of $heystack[$var1][$var2][$var3], but dynamically:

$heystack['lev1']['lev2']['lev3'] = 10;

echo getElement($heystack, array('lev1', 'lev2', 'lev3')); // you could build second parameter dynamically

function getElement($array, $indexes = array())
{
    foreach ($indexes as $index) {
        $array = $array[$index];
    }

    return $array;
}

// output: 10

You'll need to put in some defense mechanisms to make the function more robust (for elements/indexes that don't exist) but this is the basic approach.

webbiedave
  • 48,414
  • 8
  • 88
  • 101