筛选数组以保留元素计数最小、第一个和最后一个元素唯一的行


Filter array to retain rows with smallest element count and unique first and last elements

我想从数组中删除行,这样我的结果就是一个包含具有唯一第一个和最后一个元素的行的数组。如果两行(或多行)的第一个和最后一个值相同,我希望保留元素计数最低的行。

假设我有以下数组:

$var = [
    [1, 2, 3],
    [1, 3],
    [1, 2, 4, 3],
    [1, 3, 4]
];

我想要的是从$var中删除所有具有第一个和最后一个元素的数组,这些数组与$var中的另一个数组相同,但具有更多元素。

因为前三行都以1开始,以3结束,所以应该只保留包含[1, 3]的第二行。

第四行([1, 3, 4])唯一地以1开始,以4结束,因此也应该保留它。

输出应为:

[
    [1, 3],
    [1, 3, 4]
]

我正在寻找最有效的方法来做到这一点,无论是在记忆还是时间方面。$var可能有多达100个数组,每个数组中可能有多达10个元素。我曾想过在所有两个元素(for(i=0;...) for(j=i+1;...) complexCompareFunction();)之间进行某种比较,但我认为这不是很有效。

使用当前和结束

$all = array();
foreach ($var as $idx=>$arr):
  $first = current($arr);
  $last  = end($arr);
  $size  = count($arr);
  $key   = $first.'.'.$last;
  if (isset($all[$key])):
    if ($size > $all[$key]):
      unset($var[$idx]);
    else:
      $all[$key] = $size;
    endif;
  else:
    $all[$key] = $size;
  endif;
endforeach;

操作。。。您可以在最后(再次)迭代,以确保已经缩小的数组可以进一步删除

总的来说,是的,你太担心效率了(正如你在另一条评论中所想的那样)。尽管PHP并不是最快的语言,但我建议构建最简单的解决方案,并且只在最终结果出现明显问题时才考虑优化或精简它。

这是我想做的,在我的头顶上。它是基于ajreal的答案,但希望更容易遵循,并抓住一些该答案遗漏的边缘案例:

// Assume $var is the array specified in your question
function removeRedundantRoutes( $var ){
    // This line sorts $var by the length of each route
    usort( $var, function( $x, $y ){ return count( $x ) - count( $y ); } );
    // Create an empty array to store the result in
    $results = array();
    // Check each member of $var
    foreach( $var as $route ){
        $first = $route[0];
        $last = $route[ count( $route ) - 1 ];
        if( !array_key_exists( "$first-$last", $results ) ){
            // If we have not seen a route with this pair of endpoints already,
            // it must be the shortest such route, so place it in the results array
            $results[ "$first-$last" ] = $route;
        }
    }
    // Strictly speaking this call to array_values is unnecessary, but
    // it would eliminate the unusual indexes from the result array
    return array_values( $results );
}

以下是如何通过临时键(通过从给定行中的第一个和最后一个值创建分隔字符串形成)进行分组,并有条件地将符合条件的数据推送到结果数组中。循环结束后,从结果数组中提取第二列,以生成仅包含最小合格行的索引数组。无需预分拣。

代码:(演示)

$result = [];
foreach ($array as $row) {
    $cache = [count($row), $row];
    array_splice($row, 1, -1);
    $key = implode('-', $row);
    if (!isset($result[$key]) || $cache[0] < $result[$key][0]) {
        $result[$key] = $cache;
    }
}
var_export(array_column($result, 1));

替代代码:(演示)

$result = [];
foreach ($array as $row) {
    $count = count($row);
    $key = $row[0] . '-' . $row[array_key_last($row)];  // or array_pop($row)
    if (!isset($result[$key]) || $count < $result[$key][0]) {
        $result[$key] = [$count, $row];
    }
}
var_export(array_column($result, 1));

输出:

array (
  0 => 
  array (
    0 => 1,
    1 => 3,
  ),
  1 => 
  array (
    0 => 1,
    1 => 3,
    2 => 4,
  ),
)