How to get values close to the given offset of an array?

0

I'm having a hard time extracting a portion of any sequential indexed array, such as a simple range () .

The problem is that I need to specify a start offset and a limit greater than or equal to 1. This limit would control how many elements would be fetched before and after the offset data, besides itself, of course. For example:

$letras = range( 'A', 'Z' );
$offset = 3;
$limite = 2;

In this case the routine should return the fourth offset (D), two ahead (E and F) and two behind (B and C).

Whether these values change to, who knows:

$offset = 10;
$limite = 3;

The same routine should return the eleventh index (K), three ahead (L, M and N) and three behind (H, I and J).

Well, initially I turned to array_slice () believing to be the right tool for the job. However using it proved harder than it seemed because the third argument of the function works with lengths and not with offsets:

$collection = range( 'A', 'Z' );
$offset     = 10;
$limit      = 3;

$length = count( $collection );

if( $offset >= $length ) {
    throw new \OutOfRangeException( 'Requested offset exceeds the size of Collection' );
}

$start = ( $offset - $limit ) >= 0 ? ( $offset - $limit ) : 0;
$end   = $limit + 2;

$slice = array_slice( $collection, $start, $end );

I guess what I'm looking for is a mathematical solution but I'm not sure how to build logic.

Can anyone help me?

A very important additional information is that the solution, whatever it may be, also does not rely on those "wizards" that we see, for example, by inverting the array keys with their values. This is because here, in this example, I am demonstrating with a simple array filled with letters but the actual use will be a Collection of Objects, for example:

$std1 = new\stdClass;
$std1 -> name = 'Name #1';

$std2 = new\stdClass;
$std2 -> name = 'Name #2';

$std3 = new\stdClass;
$std3 -> name = 'Name #3';

$std4 = new\stdClass;
$std4 -> name = 'Name #4';

$std5 = new\stdClass;
$std5 -> name = 'Name #5';

$collection = [ $std1, $std2, $std3, $std4, $std5 ];

But, of course, not just stdClass Objects, nor Objects that already have some interface implemented, although all my Objects extend from the same base class for other reasons unrelated to this topic.

    
asked by anonymous 06.08.2017 / 01:33

2 answers

1

You can use the actual array_slice you mentioned or could use a simple for , I think the latter would be easier, you could also use next and prev ,

function selecionar($array, $comeco, $limite){

    $comeco  -= $limite + 1;
    $limite   = $limite * 2 + 1;

    for($limite; $limite > 0; $limite--){

        if(isset($array[$comeco + $limite])){
            $selecao[] = $array[$comeco + $limite];
        }

    }

    return $selecao;

}

So, using:

echo implode(',', selecionar($collection, 3, 2));

It will return exactly F,E,D,C,B .

Using array_slice without any correction (if the limit is greater than the offset will have problems, for example), you could use:

array_slice($collection, $offset - $limit, $limit * 2 + 1);

Logo:

$collection = range( 'A', 'Z' );
$offset = 3;
$limit = 2;

echo implode(',',  array_slice($collection, $offset - $limit, $limit * 2 + 1));

It would exactly result in B,C,D,E,F .

    
06.08.2017 / 02:52
1

I imagine that if you do $offset - start + $limit + 1 it should have the expected behavior. For example:

$offset = 10;
$limite = 3;

In this case $start = 7 , since $ start = $ offset - $ limit = 7 and $ end = 10 - 7 + $ limit + 1 = 7. Then the third parameter of the array_slice function will have size 7, going from the index 7 to index 13 (8 letter to 14 letter of the alphabet).

<?php
$collection = range( 'A', 'Z' );
$offset     = 20;
$limit      = 2;

$length = count( $collection );

if( $offset >= $length ) {
    throw new \OutOfRangeException( 'Requested offset exceeds the size of Collection' );
}

$start = ( $offset - $limit ) >= 0 ? ( $offset - $limit ) : 0;
$end   = $offset - $start + $limit + 1;

$slice = array_slice( $collection, $start, $end );

var_dump($slice);
    
06.08.2017 / 03:00