【问题标题】:Filter array to retain rows with smallest element count and unique first and last elements过滤数组以保留具有最少元素计数和唯一的第一个和最后一个元素的行
【发布时间】:2022-01-22 15:02:14
【问题描述】:

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

假设我有以下数组:

$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();),但我认为这不是很有效。

【问题讨论】:

  • 你有你想要做的事情的用例吗?可能有更好的方法来实现它......
  • 我正在生成连接两个用户选择位置的公共交通线路的所有组合。从生成的行组合数组(在这种情况下为$var),我想删除那些使用额外行的组合。那么,如果可以用行(1, 3) 到达第二点,为什么还要显示(1, 2, 3)?这里2是多余的,不用它也能到达目的地。
  • 也许去1,2,3会比1,3(因为你只能乘公共汽车直接去)花费更少的时间(因为你坐火车)。我们可以重构这个问题:有一个以站为节点、以连接为边的图。对于每两个节点 n,m,您现在搜索以 n 开头并以 m 结尾的最短路径。关于这个主题有大量的参考资料。
  • 好吧,如果到达目的地的唯一途径是乘坐 3 号线,那么既然您必须等待 3 号线,为什么还要费心乘坐 2 号线呢?您只需更改线路并支付额外的车票即可到达相同的目的地。还是我错过了什么?这个任务只是整个算法的一小部分,它会删除任何与另一个相似或有一些额外线路的生成路线。我没有使用最短路径,因为我将通过取决于用户何时查询我的应用程序的旅行时间来“测量”我的路径。因此,一条距离比另一条更短的路径不一定更好。
  • 使用 Djikstra 最佳第一路径或 A* 或类似算法。

标签: php arrays comparison filtering grouping


【解决方案1】:

使用currentend

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

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

【讨论】:

  • 在本例中,(1, 2, 3) 仍然存在于数组中。
  • 代码运行良好,但前提是$var 中的数组按元素数量排序。我相信首先按数组的元素数量对$var 进行排序,然后执行您发布的代码并不是很有效。还是我太担心效率了?
  • 在 $all 或其他数组中存储 $idx 的最小尺寸,并在最里面的 else 中为这个 $idx 添加 unset。
  • 我已经按预期完成了这项工作,谢谢您的回答!不幸的是,很难决定将哪个答案标记为最佳,尤其是在这种情况下。我的选择是关于答案的细节,而不是答案的质量。
【解决方案2】:

一般来说,是的,您太担心效率了(正如您在另一条评论中所想的那样)。尽管 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 );
}

【讨论】:

  • 通过一些调整,我得到了这个工作。感谢您的帮助和回答中的详细信息!
【解决方案3】:

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

代码:(Demo)

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

替代代码:(Demo)

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

【讨论】:

    猜你喜欢
    • 2017-02-27
    • 2013-01-14
    • 2023-04-06
    • 1970-01-01
    • 2011-06-06
    • 2013-12-01
    • 2022-12-13
    • 1970-01-01
    相关资源
    最近更新 更多