将列表拆分为N个部分

时间:2018-05-15 08:26:10

标签: elixir

给定一个列表,如何将其拆分为N个子列表?他们不一定必须具有相同的规模。例如,给定9个元素并将其分成N = 3个子列表=> 3×3。或者进入N = 4个子列表=> 2,2,2和1。

我该怎么做? Elixir库中没有函数吗?

Enum.split将列表拆分为2部分

更新

如果我有7个元素并且我想将它们分成3个子列表,那么应该创建3个子列表:

[[3 elements], [2 elements], [2 elements]] 

即,我想保留所有元素

3 个答案:

答案 0 :(得分:3)

您可以考虑使用Enum.chunk_every将列表拆分为每个n个元素的子列表:

some_list = [1, 2, 3, 4, 5, 6]
Enum.chunk_every(some_list, 2)
[[1, 2], [3, 4], [5, 6]]

首先计算列表的总长度:

total_length = length(some_list)

并将该数字除以所需的部件数量,以获得每个部件的长度:

desired_amount_of_sublists = 3
chunk_length = Integer.floor_div(total_length, desired_amount_of_sublists)

应允许您随意将列表分块为您需要的部分:

Enum.chunk_every(some_list, chunk_length)
[[1, 2], [3, 4], [5, 6]]

如果你很难要求每个子列表都是n个元素,那么你可以传递选项:discard以丢弃最后一个子列表,如果它少于n个元素:

Enum.chunk_every([1,2,3,4,5,6,7], 2, 2, :discard)
[[1, 2], [3, 4], [5, 6]]

如果你有硬性要求,你不能丢弃任何元素,例如你需要将余数元素与第一个子列表合并,那么你可以这样做:

说到上面你得到了:

result_so_far = Enum.chunk_every([1,2,3,4,5,6,7], 2)
[[1, 2], [3, 4], [5, 6], [7]]

首先反转result_so_far,然后取出第一个子列表,即[7],如下所示:

[last_sublist | other_sublists] = Enum.reverse(result_so_far)

然后检查last_sublist的长度。如果它对应chunk_length,那么您没问题,result_so_far具有所需的结果。 如果它小于chunk_length,则需要将其元素与result_so_far的第一个子列表合并,您可以按如下方式执行此操作: [first_sublist | rest ] = Enum.reverse(other_sublists)

然后

[Enum.concat(first_sublist, last_sublist) | rest]应该呈现

[[1, 2, 7], [3, 4], [5, 6]]

答案 1 :(得分:0)

我们假设我们需要3个块:

list = [1, 2, 3, 4, 5, 6, 7]
chunk_count = 3
chunk_length =
  list
  |> Enum.count()
  |> div(chunk_count)


list
|> Enum.with_index() # to calculate position
|> Enum.reduce(%{}, fn {e, i}, acc ->
     Map.update(acc, rem(div(i, chunk_length), chunk_count), [e], &([e | &1]))
   end)
|> Map.values()      # since we do need lists
|> Enum.map(&Enum.reverse/1)
#⇒ [[1, 2, 7], [3, 4], [5, 6]]

如果Elixir中的列表要实现Access行为,则代码会更简单,因为我们可以使用Kernel.put_in/3

答案 2 :(得分:0)

找到了这个问题,但当前的答案都不适用于我的用例,为此,我希望组的大小尽可能一致。例如:

Enum.to_list(1..100) |> chunk_uniformly(6) |> Enum.map(&length/1)

# desired result is [17, 17, 17, 17, 16, 16]
# current accepted answer returns [20, 16, 16, 16, 16, 16]

这是我最终的解决方法:

def chunk_uniformly(list, number_of_chunks) do
  len = length(list)

  optimal_chunk_size = div(len + number_of_chunks - 1, number_of_chunks)

  overspill = optimal_chunk_size * number_of_chunks - len

  {full_chunks, partial_chunks} =
    list
    |> Enum.split((number_of_chunks - overspill) * optimal_chunk_size)

  Enum.concat(
    full_chunks |> Enum.chunk_every(optimal_chunk_size),
    case partial_chunks do
      [] -> List.duplicate([], overspill)
      ls -> ls |> Enum.chunk_every(optimal_chunk_size - 1)
    end
  )
end

如果像在我的用例中那样,您实际上是在最大块大小的情况下工作的,则可以得到如下的块数:

number_of_chunks = div(length(list) + max_chunk_size - 1, max_chunk_size)