【问题标题】:How to group a list of list that matches certain condition如何对符合特定条件的列表进行分组
【发布时间】:2022-02-14 14:45:12
【问题描述】:

我有一个列表。

[
  [4, 5, 6], 
  [5, 6, 7], 
  [8, 9, 10], 
  [20, 21, 22],
  [22,23,24]
]

如果 [5,6,7] 的 upper_bound(5) 小于或等于 [4,5,6] 的 lower_bound(6) 则将它们合并在一起,最终列表应如下所示。

[
  [
    [4, 5, 6], 
    [5, 6, 7]
  ],
  [8, 9, 10],
  [ 
    [20, 21, 22],
    [22,23,24]
  ]

]

我该怎么做?

【问题讨论】:

  • 我有点困惑。你把upper_boundlower_bound混在一起了吗?
  • @AstridE。在列表 [6,7,8] 中,不是 upper_bound 6 和 lower_bound 8 吗?还是我弄错了?
  • 上限应表示您的集合中的最大值(如“此集合中没有大于上限的值”);下限反之亦然。我不确定在此处引用哪个来源最准确,但例如Wolfram MathWorld 有定义。

标签: algorithm elixir


【解决方案1】:

此答案假定项目始终是连续的,如您的示例所示,并且没有列表完全包含另一个列表。

首先,最好使用Ranges,因此我们首先将列表转换为范围。

然后我们可以编写一个递归算法group_ranges/1,它检查每个范围与它的前任。如果重叠(使用Range.disjoint?/2),请将其添加到组中。如果不重叠,则开始一个新组。

最后,由于您希望不重叠的范围独立而不是一组,因此我们会在分组完成后处理这种情况。如果这是严格要求,您还可以在此阶段将范围转换回列表。

defmodule Example do
  def run do
    [[4, 5, 6], [5, 6, 7], [8, 9, 10], [20, 21, 22], [22, 23, 24]]
    |> Enum.map(fn [head | tail] -> head..List.last(tail) end)
    |> group_ranges()
    |> Enum.map(fn
      [single] -> single
      group -> group
    end)
  end

  def group_ranges([current | rest]), do: do_group_ranges(rest, [current], [])

  defp do_group_ranges([], group, grouped) do
    Enum.reverse([Enum.reverse(group) | grouped])
  end

  defp do_group_ranges([current | rest], [prev | _] = group, grouped) do
    if Range.disjoint?(current, prev) do
      # Create a new group
      do_group_ranges(rest, [current], [Enum.reverse(group) | grouped])
    else
      # Add to existing group
      do_group_ranges(rest, [current | group], grouped)
    end
  end
end

结果:

iex(1)> Example.run
[[4..6, 5..7], 8..10, [20..22, 22..24]]

【讨论】:

    【解决方案2】:

    假设结果列表的数量少于 32,按 Range 分组将起作用。

    input
    |> Enum.reduce(%{}, fn list, acc ->
      {min, max} = Enum.min_max(list)
    
      acc |> Map.keys() |> Enum.reduce_while(nil, fn 
        mn..mx, _ when min >= mn and min <= mx and max >= mn and max <= mx -> 
          {:halt, Map.update(acc, mn..mx, & [list | &1])}
        mn..mx, _ when min >= mn and min <= mx -> 
          {:halt, acc |> Map.delete(mn..mx) |> Map.put(mn..max, [list | Map.get(acc, mn..mx)])}
        mn..mx, _ when max >= mn and max <= mx ->
          {:halt, acc |> Map.delete(mn..mx) |> Map.put(min..mx, [list | Map.get(acc, mn..mx)])}
        _, _ -> {:cont, nil}
      end) || Map.put(acc, min..max, [list])
    end)
    |> Map.values()
    |> Enum.map(&Enum.reverse/1)
    

    如果预期的列表数量大于 32 并且必须保留排序,则解决方案会更加麻烦。

    【讨论】:

      猜你喜欢
      • 2022-11-19
      • 2018-05-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-04-07
      相关资源
      最近更新 更多