1

What is the most efficient way to get a subarray of an array between two keys.

So for example,

$arr=array();
$arr['2014-03-01']='something';
$arr['2014-03-03']='something';
$arr['2014-02-04']='something';
$arr['2014-03-05']='something';
$arr['2014-03-07']='something';
$arr['2014-03-09']='something';
$arr['2014-01-04']='something';
$arr['2014-03-31']='something';

Get the subarray between two keys i.e. start key:2014-02-04 and end key:2014-03-07 should return an array with only:

$arr['2014-02-04']='something';
$arr['2014-03-05']='something';
$arr['2014-03-07']='something';

Is there a quick and efficient way to do this without looping through the entire array?

UPDATE: I did a benchmark here is the results:

$arr=array();
for ($i=1;$i<=1000000;$i++) {
    $arr["$i"]=$i;
}

$time_start=microtime_float();

$start = '20000';
$end   = '20010';

$offset = array_search($start, array_keys($arr));
$length = array_search($end, array_keys($arr)) - $offset + 1;
$output = array_slice($arr, $offset, $length);
print_r($output);
$time_end = microtime_float();
$time = $time_end - $time_start;
echo "TIME=$time\n";
echo "\n============\n";
$time_start=microtime_float();

$result = array();
$start = '20000';
$end   = '20010';

foreach ($arr as $key => $value) {
  if ($key >= $start && $key <= $end)
    $result[$key] = $value;
}
print_r($output);
$time_end = microtime_float();
$time = $time_end - $time_start;
echo "TIME=$time\n";

exit;

RESULTS:

Array
(
    [0] => 20000
    [1] => 20001
    [2] => 20002
    [3] => 20003
    [4] => 20004
    [5] => 20005
    [6] => 20006
    [7] => 20007
    [8] => 20008
    [9] => 20009
    [10] => 20010
)
TIME=1.8481030464172

============
Array
(
    [0] => 20000
    [1] => 20001
    [2] => 20002
    [3] => 20003
    [4] => 20004
    [5] => 20005
    [6] => 20006
    [7] => 20007
    [8] => 20008
    [9] => 20009
    [10] => 20010
)
TIME=1.700336933136

Hence, a simple loop seems to be slightly faster. The advantage increases if I make the start further down the array. You could also use break; once the latter point is reached.

5
  • array_filter "Iterates over each value" so not prefered Commented Mar 19, 2014 at 8:29
  • Umm, well, unless the keys are sorted in advance, there is no way you can forego the 'iterates over each values' problem. Commented Mar 19, 2014 at 8:30
  • keys are sorted as above. Commented Mar 19, 2014 at 8:31
  • You're not actually trying to get a "subarray between two keys"! You're trying to filter your data set to only include values between two boundaries, which is a different problem. Also one you'll never solve without looking at each value individually in a loop. Commented Mar 19, 2014 at 8:43
  • i will benchmark the answers and post the results. Commented Mar 19, 2014 at 8:44

2 Answers 2

4

The most efficient way is to use a loop.

$result = array();
$start = '2014-02-04';
$end = '2014-03-07';

foreach ($arr as $key => $value) {
  // your date format is string comparable, otherwise use strtotime to convert to unix timestamp.
  if ($key >= $start && $key <= $end) {
    $result[$key] = $value;
  }
}

Or less efficient way is using array_flip to exchange the key and value, then use array_filter to the required keys, then use array_intersect_key to get the result.

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

8 Comments

using a huge array. anyway to do this without looping?
@Ray You will never be able to work with any array without looping.
@xdazz Only works as long as the keys are comparable and sorted though. Not necessarily a general solution.
@deceze The op's date format could be compared as string. And sorted? do you mean sort the result?
@xdazz I mean, what about the array ['f' => 'foo', 'd' => 'bar', 'z' => 'baz']? Your solution won't work with that. Just noting that though, since it works for the OP's case.
|
2

You can try with ksort, array_slice and array_search:

$start = '2014-02-04';
$end   = '2014-03-07';

ksort($arr);
$offset = array_search($start, array_keys($arr));
$length = array_search($end, array_keys($arr)) - $offset + 1;
$output = array_slice($arr, $offset, $length);

var_dump($output);

Output:

array (size=5)
  '2014-02-04' => string 'something' (length=9)
  '2014-03-01' => string 'something' (length=9)
  '2014-03-03' => string 'something' (length=9)
  '2014-03-05' => string 'something' (length=9)
  '2014-03-07' => string 'something' (length=9)

5 Comments

This solution assumes that the array is sorted. :)
@Achrome It is. Look at ksort.
I am aware of ksort. I doubt that it's slower than a simple loop construct.
Question just out of curiosity - is that method anyhow faster than simple foreach? I mean wont ksort, array_slice, array_search loop that array "in the background" anyway a few times?
This will be looping a whole lot.

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.