0

Given a simple array of 10 delicious fruits:

$fruits = [
    'apple',
    'kumquat',
    'pomegranate',
    'jujube',
    'plum',
    'passon_fruit',
    'pineapple',
    'strawberry',
    'raspberry',
    'grapefruit',
];

I would like to retrieve a specific range/spread, using a given key as a reference. The range should always be the exact length, so the starting point might differ.

Easiest example from the middle: key is 4 (plum) with length 5 would result in:

$result = [
    'pomegranate',
    'jujube',
    'plum', // reference
    'passon_fruit',
    'pineapple',
];

However, with key 1 (kumquat), we only have item 0 left and need 4 more at the end:

$result = [
    'apple',
    'kumquat', // reference
    'pomegranate',
    'jujube',
    'plum',
];

Same goes for key 8 (raspberry), which needs more items from the beginning of the array:

$result = [
    'passon_fruit',
    'pineapple',
    'strawberry',
    'raspberry', // reference
    'grapefruit',
];

Edit

As suggested by @berend, here's what I tried. Please note that this does not fully do what I intended:

function getRange(array $array, int $ref, int $spread)
{
    $min = $ref - round(($spread / 2), 0, PHP_ROUND_HALF_DOWN);
    $max = $ref + round(($spread / 2), 0, PHP_ROUND_HALF_UP);

    if ($min < 0) {
        $max += -$min;
        $min = 0;
    }
    
    if ($max > (count($array) - 1)) {
        $min = $min - ($max - count($array) - 1);
        
        // not enough items
        if ($min < 0) {
            $min = 0;
        }
    }

    return array_intersect_key($array, range($min, $max));
}

var_dump(getRange($fruits, 4, 5));
var_dump(getRange($fruits, 1, 5));
var_dump(getRange($fruits, 8, 5));
4
  • 1
  • 1
    If you want to reference the offset counted from the middle of the desired range (why?), how will you deal with even lengths? Suppose length 4 with plum as the reference, will you get 1 before and 2 after, or vice versa? Commented Sep 27, 2021 at 20:14
  • @berend, I added my attempt. Commented Sep 27, 2021 at 20:26
  • @Markus AO, I am trying to retrieve a snippet of the full array of items, whilst showing some context. You are correct that with even lengths the spread needs to be rounded. Commented Sep 27, 2021 at 20:26

1 Answer 1

1

I think it can be simplified by using array_slice instead. Then you don't need to get the max and make another array.

function getRange(array $array, int $ref, int $spread)
{

    // calculate an offset using your original method
    // use zero if you end up with a negative offset
    $min = max(0, $ref - round(($spread / 2), 0, PHP_ROUND_HALF_DOWN));

    // if the offset puts the range beyond the end of the array, recalculate 
    // it by subtracting the spread size from the array size
    // again, use zero if you end up with a negative offset
    if ($min + $spread > count($array)) {
        $min = max(0, count($array) - $spread);
    }

    return array_slice($array, $min, $spread);
}

If you need to preserve the keys, you can use the fourth argument of array_slice for that.


I think part of why yours isn't working is that you're using array_intersect_key, but in the range you're using as the second argument, the numbers you've calculated are the values, not the keys. There are a couple of other issues, but that looks like the main problem.

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

1 Comment

This is perfect and super neat, thank you so much! array_slice is way nicer than creating another array like I did. Just one thing; I added a cast for $min to (int) in the array_slice, as this seems to be a float sometimes.

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.