Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

the fastest way to pick the Nth element of a hash

Tags:

arrays

php

I've got a big hashtable (array with string indexes) and looking for a function that quickly picks the first (ideally, also Nth) element from it. array_shift() and reset() are too slow for my needs.

UPDATE: i'm also not looking for a reference-based solution, the function should accept expressions as in get_first(some_func_returning_array())

ANSWER array_slice method (kudos Gumbo) seems to be the winner. Complete benchmarking code

function bigary($n) {
    $a = array();
    $s = range('A', 'Z');
    do {
        shuffle($s);
        $a[substr(implode('', $s), rand(10, 20))] = $n;
    } while(--$n);
    return $a;
}

function timeit($name, $fn) {
    global $results;

    $loops = 1000;
    $size  = 5432;

    static $a;
    if(!$a) $a = bigary($size);

    $t = microtime(1);
    for($i = 0; $i < $loops; $i++)
        $b = $fn($a);
    $results[$name] = microtime(1) - $t;
}

timeit('dummy', function ($a) { 
    // benchmark php function call overhead
});

timeit('array_shift', function ($a) { 
    return array_shift($a); 
});

timeit('reset', function ($a) { 
    return reset($a); 
});

timeit('foreach', function ($a) { 
    foreach($a as $b) return $b;
});

timeit('keys', function ($a) { 
    $b = array_keys($a); 
    return $a[$b[0]];
});

timeit('values', function ($a) { 
    $b = array_values($a); 
    return $b[0];
});

timeit('slice', function ($a) { 
    $b = array_slice($a, 0, 1); 
    return reset($b);
});

asort($results);

foreach($results as $name => $time)
    printf("%20s = %.3f\n", $name, $time);

Results:

           dummy = 0.393
           slice = 0.433
          values = 0.824
         foreach = 0.929
           reset = 0.935
     array_shift = 0.954
            keys = 1.371
like image 362
user187291 Avatar asked Jun 07 '11 09:06

user187291


2 Answers

Use array_slice to get an array of just the n-th item and array_pop to finally get it:

$nthItem = array_pop(array_slice($arr, $n, 1));
like image 120
Gumbo Avatar answered Sep 20 '22 12:09

Gumbo


Your benchmark may be flawed, since:

$fn = function ($a) { 
    return array_shift($a); 
};
timeit('array_shift', $fn);

array_shift = 1.242 (5432)

and

timeit('array_shift', array_shift);

array_shift = 0.026 (4433)

but also

$fn = function ($a) { } 
timeit('empty lambda', $fn);

empty lambda = 0.501 (0)

That being said, another possible solution:

$v = array_values($a);
return $v[ $index ];

Sample Code:

$t = microtime(1);
$v = array_values($a); // cached
while($loops--) {
    $b = $v[$loops];
}

array_values = 0.002 (5432)

like image 38
J.C. Inacio Avatar answered Sep 22 '22 12:09

J.C. Inacio