【问题标题】:Get offset neighbors of an array获取数组的偏移邻居
【发布时间】:2017-08-04 20:06:09
【问题描述】:

TL;DR

如何对数组进行切片以在给定偏移量(也是用户定义)之前和之后获取(用户定义的)条目数而不会超出范围。例如:

$collection = [ 'A', 'B', 'C', 'D', 'E' ];

如果定义了 2 的限制和索引 3 (D) 的起始偏移量,则例程应返回 BCDED 之前有两个,@ 之后只有一个987654328@,否则会超出范围。

如果定义了 3 的限制和索引 0 (A) 的起始偏移量,则例程应返回 ABCD,在 A 之后返回三个,在它之前没有返回,否则也会超出范围。

切片必须扩展以始终在$offset 找到的条目之前和之后带来$limit 定义的元素数量。当然,除非其中一方超出范围


我正在尝试找出一种简单的(可能是唯一的数学方法)来获取索引(并且当前是顺序)数组的偏移邻居,但具有自定义限制。

我想到的第一件事就是使用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,依此类推 o.O

我试图寻找解决方案,但到目前为止我能找到的是涉及循环和复杂条件。据我所知,我不应该需要它,毕竟如果它是连续的,我可以对其应用“简单”数学。

但是我的没用,所以我可能错了......

【问题讨论】:

  • 你想做什么?一个都看不懂
  • 我已经用问题的更总结版本编辑了问题
  • 第一个例子你取D,那为什么不取第二个例子A呢?
  • @procras 你写的似乎是正确的。在第一个例子中,起始位置在数组中,在第二个例子中它被省略了。哪个是正确的?
  • 你的意思是在我删除我的尝试(上半部分)的修订之前还是之后。因为我多次检查了我在短版中写的 3 次,据我所知是正确的

标签: php arrays math offset


【解决方案1】:

再次编辑。
这可能是您可以实现此功能的最轻量级。
它将开始和结束值与最大值和最小值进行比较,以确保它不会上溢/下溢。
输出它使用 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);



编辑我想我找到了最好的解决方案。预匹配。

$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} - 再次匹配零到六次之间的任何内容。

在正则表达式模式中,我使用 chr() 将数字 ($offset) 转换为大写字母。
在这种情况下,偏移量为 1,因此表示字母表中的第二个字母(A 为 0)。
$limit 用于限制正则表达式搜索。 {0, <$limit>} 这意味着在 0 和 $limit 之间尽可能多地匹配。

https://3v4l.org/BVdiF

根本没有计算或数组函数。只是一个正则表达式。


不确定这次我是否正确。
但是根据偏移限制上溢还是下溢来计算数组切片的开始值和结束值。

$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);

https://3v4l.org/0l1J6

编辑发现一些问题。
如果 start 为 0,我需要添加 1,否则不需要。
最大值的结束限制没有正常工作,感谢您指出这一点。
如果限制为 0 错误消息。

我想我现在所有变体都可以工作了。

【讨论】:

  • 关闭。在我压制的所有试图保持帖子简短的尝试中,我或多或少地遇到了这样的事情。请注意,您的$limit 的行为与array_slice() 通常所期望的非常相似:数组中的最大元素数,而它应该是所选偏移量的条目加上$limit 在 AND @ 之前定义的元素数987654335@。除非超出范围,否则可以少(或没有)
  • 我想我在阅读您的帖子之前就注意到了我的错误。我忘了在 $end 中添加 +1。输入 2/2 = ABCDE 的正确答案是什么?如果是这样,我想我现在明白了。
  • 是的,如果$offset2,它应该在它之前带来另外两个偏移量(AB)和它之前的两个偏移量(C 和@987654341 @)
  • 在我选择 E 偏移量 (4) 之前它工作正常。它只返回C 而不是CDE
  • 我猜这么短的数组有太多误报的机会。例如,使用像 range() 我一直用来测试的更大的数组(原始帖子),您的方法适用于指定偏移量之前的元素数量,但它导致之前的元素数量增加一倍,看:3v4l.org/BgFaH
【解决方案2】:

这是您最简洁的方法 - 不需要条件计算。

代码:(Demo)

$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',
)

我可以确认array_fill_keys()array_fill() 一样有效。这是另一个演示:

代码:(Demo)

$collection = range('A', 'Z');
for ($i = 0; $i < 5; ++$i) {
    $offset = rand(0, 25);
    $limit = rand(1, 5);
    echo "Offset: $offset\nLimit: $limit\n";
    var_export(array_intersect_key($collection, array_fill_keys(range($offset-$limit, $offset+$limit), null)));
    echo "\n---\n";
}

输出:

Offset: 13
Limit: 3
array (
  10 => 'K',
  11 => 'L',
  12 => 'M',
  13 => 'N',
  14 => 'O',
  15 => 'P',
  16 => 'Q',
)
---
Offset: 0
Limit: 5
array (
  0 => 'A',
  1 => 'B',
  2 => 'C',
  3 => 'D',
  4 => 'E',
  5 => 'F',
)
---
Offset: 12
Limit: 2
array (
  10 => 'K',
  11 => 'L',
  12 => 'M',
  13 => 'N',
  14 => 'O',
)
---
Offset: 25
Limit: 3
array (
  22 => 'W',
  23 => 'X',
  24 => 'Y',
  25 => 'Z',
)
---
Offset: 11
Limit: 5
array (
  6 => 'G',
  7 => 'H',
  8 => 'I',
  9 => 'J',
  10 => 'K',
  11 => 'L',
  12 => 'M',
  13 => 'N',
  14 => 'O',
  15 => 'P',
  16 => 'Q',
)
---

【讨论】:

  • $offset$limit 使用随机值运行一些测试,所有测试都有效。一件事引起了我的注意:为什么没有验证?是不需要它们,还是您只是跳过它们进行 sn-p?
  • 如果您不信任用户输入,您可以包含您的投掷条件。如果有人使用$offset=26$limit=1 怎么办?他们收到Z 吗?还是你想让它打破?这是您的条件检查唯一需要考虑的事情。
  • 我在这里重新考虑,我相信没有必要。感谢您的解决方案:)
  • @Bruno 看看我的更新。我认为它更简单。
  • 我投了反对票有两个原因 1. 你没有做功课。但我可以接受,如果不是因为对方,我会放手的。 2. 你对你的答案没有任何解释或cmets。这是答案的重要组成部分。你应该知道。够了。但我的数组不是多维的。又是作业……
猜你喜欢
  • 2018-01-12
  • 2017-12-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多