Как я могу нарезать массив, чтобы получить (определяемое пользователем) количество записей до и после заданного смещения (также определяемого пользователем) без выхода за пределы диапазона. Например:
$collection = [ 'A', 'B', 'C', 'D', 'E' ];
Если задано ограничение 2 и начальное смещение индекса 3 (D), процедура должна возвращать B
, C
, D
, E
: два перед D
и только один после D
, иначе он выйдет за пределы диапазона.
Если определено ограничение 3 и начальное смещение индекса 0 (A), подпрограмма должна возвращать A
, B
, C
, D
, три после A
и ни один до нее, иначе она также выйдет за пределы диапазона.
Кусочек должен расширяться, чтобы всегда приводить количество элементов, определяемых
$limit
до и после записи, найденной в$offset
. Если одна из сторон не выходит за пределы диапазона, конечно
Я пытаюсь понять простой, возможно, математический способ, чтобы получить смещенные соседи индексированного (и в настоящее время последовательного) массива, но с пользовательским лимитом.
Первое, что пришло ко мне, это использовать array_slice () :
$collection = range( 'A', 'Z' ); $offset = 0; //rand( 0, 25 ); $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 );
Из того, что я мог проверить, $start
работает нормально. Я заставляю первое смещение избегать отрицательного.
Тогда я сначала подумал, что могу просто увеличить $limit
на 2, чтобы иметь «второе смещение», и, конечно, это не сработало, но это была моя самая ошибка. Я не часто использую эту функцию>. <
Затем я изменил логику $end
на:
$end = $offset + $limit + 1;
Смещение, которое я нашел в коллекции, плюс предел и еще один, чтобы включить самую запись, хранящуюся в $offset
. Он работал нормально до тех пор, пока $offset
будет установлен на 4 (E) или больше. После этого, для буквы «F» ломтик поднимался до G, H до I и так далее oO
Я попытался найти решение, но то, что я мог найти до сих пор, связано с циклами и сложными условиями. Насколько я могу судить, мне это не нужно, ведь если это последовательное, я могу применить «простую» математику.
Но мой не работал, поэтому я могу ошибаться …
Это ваш самый чистый подход – никаких условных вычислений не требуется.
Код: ( Демо )
$collection = range( 'A', 'Z' ); $offset = 1; //rand( 0, 25 ); $limit = 3; $indices=array_flip(range($offset-$limit,$offset+$limit)); // genereate offset range and flip values to keys //var_export($indices); /*array ( -2 => 0, -1 => 1, 0 => 2, 1 => 3, 2 => 4, 3 => 5, 4 => 6, )*/ var_export(array_intersect_key($collection,$indices)); // retain values with listed keys
Вывод:
array ( 0 => 'A', 1 => 'B', 2 => 'C', 3 => 'D', 4 => 'E', )
Изменить снова.
Это, вероятно, так же легко, как вы можете сделать эту функцию.
Он сравнивает начальные и конечные значения с max и min, чтобы убедиться, что он не переполняется / нисходит.
Для вывода он использует substr ().
Но вы также можете зацикливать его или взорвать. См. Ссылку для этого кода.
https://3v4l.org/sfCKY
$alpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; $offset = 1; $limit = 6; $start = max(0, $offset-$limit); //if offset-limit is negative value max() makes it 0 $end = min(strlen($alpha)-1, $offset+$limit); // if offset+limit is greater than str lenght min will make it str lenght. Echo substr($alpha, $start, $end-$start+1);
Редактировать Я думаю, что нашел лучшее решение. Preg_match.
$collection = range( 'A', 'Z' ); $offset = 1; $limit = 6; $alpha = implode("", $collection); Preg_match("/.{0," . $limit ."}" . chr(65 +$offset) . ".{0," . $limit ."}/", $alpha, $match); Var_dump($match);
Извините за отсутствие объяснения.
Я создаю шаблон регулярного выражения, похожий на .{0,6}B.{0,6}
но 6 и B переменны от входов. Это означает:
.{0,6}
– сопоставить что угодно между нулем и шестью раз.
B
– буквально «B».
.{0,6}
– повторить все, что угодно, от нуля до шести раз.
В шаблоне regex я использую chr () для преобразования числа ($ offset) в заглавную букву.
В этом случае смещение равно 1, что означает вторую букву в алфавите (A равно 0).
Предел $ используется, чтобы ограничить поиск регулярных выражений. {0, <$limit>
} означает совпадение с числом до 0 и $.
Никаких вычислений или функций массива вообще. Просто регулярное выражение.
Не уверен, правильно ли я на этот раз.
Но вычислите начальные и конечные значения среза массива в зависимости от того, переполняете ли вы или переполняете лимит смещения.
$collection = [ 'A', 'B', 'C', 'D', 'E' ]; $offset = 4; $limit = 2; If($offset>count($collection) || $offset < 0 || $limit ==0) die("inputs out of bounds"); If($offset-$limit >=0){ $start = $offset-$limit; }else{ $start =0; } If($offset+$limit>count($collection)){ $end = count($collection)-$offset+$limit; }else{ $end = $offset + $limit; } If($start ==0) $end++; $result = array_slice($collection, $start, $end-$start+1); Var_dump($result);
Редактировать нашли некоторые проблемы.
Если start 0, мне нужно добавить 1, а не иначе.
Конечный предел max не работал должным образом, спасибо, что указали это.
Если предел равен 0 сообщению об ошибке.
Думаю, теперь у меня все изменения.
Объяснения здесь: https://stackoverflow.com/a/45532145/1636522 .
Кстати, я избил их всех: https://3v4l.org/5Gt7B/perf#output 😛
Версия PHP:
$padding = 2; $letters = range('A', 'Z'); $indexes = ['-1', ' 0', ' 1', ' 2', '13', '24', '25', '26']; foreach ($indexes as $index) { $i = intval($index); echo $index . ' ' . ( isset($letters[$i]) ? $letters[$i] : '/' ) . ' [' . implode(', ', array_slice( $letters, $i - min($i, $padding), $padding + 1 + min($i, $padding) )) . ']' . "\n"; }
Версия JS:
padding = 2; letters = new Array(27).join(" ").split(""); letters = letters.map((x, i) => (i + 10).toString(36)); indexes = ["-1", " 0", " 1", " 2", "13", "24", "25", "26"]; for (i = 0; i < indexes.length; i++) { index = parseInt(indexes[i], 10); s = letters.slice( index - Math.min(index, padding), padding + index + 1 ); console.log( indexes[i], letters[index] || "/", "[" + s.join(", ") + "]" ); }