按子数组的总长度分割子数组的数组(Ruby)

3

我有一个子数组的数组:

arr = [["a", "b", "c"], ["a", "b"], ["a", "b", "c"], ["a", "c"],
       ["c", "v"], ["c", "f"], ["e", "a"], ["a", "b", "v"],
       ["a", "n", "c"], ["a", "b", "m"], ["a", "c"], ["a", "c", "g"]]

我希望将每个子数组的元素放入另一个数组中,但子数组大小之和必须小于或等于6。因此,我希望得到这样的结果。
[["a", "b", "c", "a", "b"], ["a", "b", "c", "a", "c"],
 ["c", "v", "c", "f", "e", "a"], ["a", "b", "v", "a", "n", "c"],
 ["a", "b", "m", "a", "c"], ["a", "c", "g"]]

我现在的代码是

stop = 0
new_arr = []
indexo = ""
arr.each_with_index do |x, index|
   stop = stop + x.size
   if stop <= 6
      new_arr << x
      indexo = index
   end
end

我之所以卡在这里,是因为我的代码只取了前两个元素。原始数组有大约1000个子数组,而我的代码没有按那种形式进行分割。


不是的,我的代码获取了所有元素,但对于第三个及以后的元素并没有做太多处理,因为“stop”变量只会增加(而第三个元素使其超过6)。 - Sergio Tulentsev
2个回答

2
您可以使用reduce方法并将子数组推入新数组中。请考虑以下内容:
new_arr = arr.reduce([]) do |acc, sub_array|
  last_element = acc[acc.length - 1]

  if last_element.nil? or (last_element + sub_array).length > 6
    acc << sub_array
  else
    acc[acc.length - 1] = last_element + sub_array
  end
  acc
end

# Tests
new_arr.flatten.size == arr.flatten.size # test total number of elements in both the arrays
new_arr.map(&:size) # the sizes of all sub arrays
new_arr.map(&:size).min # min size of all sub arrays
new_arr.map(&:size).max # max size of all sub arrays

如果代码不清晰,请告诉我。

更新:

reduce方法会通过遍历枚举对象的每个元素来将其“减少”为单个值,就像eachmap一样。

考虑一个例子:

# Find the sum of array
arr = [1, 2, 3]

# Reduce will accept an initial value & a block with two arguments
#   initial_value: is used to set the value of the accumulator in the first loop

#   Block Arguments:
#   accumulator: accumulates data through the loop and finally returned by :reduce
#   value: each item of the above array in every loop(just like :each)

arr.reduce(0) do |acc, value|
  # initial value is 0; in the first loop acc's value will be set to 0
  # henceforth acc's value will be what is returned from the block in every loop

  acc += value
  acc # acc is begin returned; in the second loop the value of acc will be (0 + 1)
end

在这种情况下,每次循环中,我们将项目的值添加到累加器中,并返回累加器以供下一次循环使用。一旦 reduce 迭代了数组中的所有项,它就会返回累加器。
Ruby 还提供了语法糖,使其看起来更加高端:
arr.reduce(:+) # return 6

这是一篇关于IT技术的好文章,供您参考:文章链接

以您的问题为例:

# Initial value is set to an empty array, what we're passing to reduce
new_arr = arr.reduce([]) do |acc, sub_array|
  # In the first loop acc's value will be set to []

  # we're finding the last element of acc (in first loop since the array is empty
  #    last element will be nil)
  last_element = acc[acc.length - 1]

  # If last_element is nil(in first loop) we push the first item of the array to acc
  # If last_element is found(pushed in the previous loops), we take it and sum
  #    it with the item from the current loop and see the size, if size is more
  #    than 6, we only push the item from current loop
  if last_element.nil? or (last_element + sub_array).length > 6
    acc << sub_array
  else
    # If last element is present & last_element + item from current loop's size
    #    is less than 6, we push the (last_element + item from current loop) into 
    #    the accumulator.
    acc[acc.length - 1] = last_element + sub_array
  end

  # Finally we return the accumulator, which will be used in the next loop
  # Or if has looped through the entire array, it will be used to return back
  #    from where it was called
  acc
end

你正在使用 reduce 作为 each 来引起副作用。不要这样做。 :) 我们可以很容易地重写它,使 reduce 不会引起副作用并直接返回最终数组。 - Sergio Tulentsev
非常感谢,Sergio!我已经更新了代码,将其缩小成一个数组。 :) 希望能更多地了解副作用。任何参考资料都将不胜感激! - Kumar
@SergioTulentsev 没有提到你。 :P - Kumar
啊,是的,那正是我想表达的 :) 而且,通过“副作用”,我指的是你的 reduce 块正在改变外部状态(即数组),而不仅仅使用累加器和当前元素。 - Sergio Tulentsev
明白了。:) 非常感谢您的输入。干杯! - Kumar
显示剩余2条评论

2
arr = [["a", "b", "c"], ["a", "b"], ["a", "b", "c"], ["a", "c"],
       ["c", "v"], ["c", "f"], ["e", "a"], ["a", "b", "v"],
       ["a", "n", "c"], ["a", "b", "m"], ["a", "c"], ["a", "c", "g"]]

arr.each_with_object([[]]) do |a,ar|
  if a.size + ar[-1].size > 6
    ar << a
  else
    ar[-1] += a
  end
end
  #=> [["a", "b", "c", "a", "b"], ["a", "b", "c", "a", "c"],
  #    ["c", "v", "c", "f", "e", "a"], ["a", "b", "v", "a", "n", "c"],
  #    ["a", "b", "m", "a", "c"], ["a", "c", "g"]]

步骤如下:
enum = arr.each_with_object([[]])
  #=> #<Enumerator: [["a", "b", "c", "a", "b"], ["a", "b"],...
  #     ["a", "c", "g"]]:each_with_object([[]])>

第一个值由此枚举器生成,传递给块并通过应用数组分解到传递给块的两个元素数组上进行赋值。

a, ar = enum.next
   #=> [["a", "b", "c"], [[]]] 
a  #=> ["a", "b", "c"] 
ar #=> [[]] 

请参阅 Enumerator#next。接着将评估条件语句。
a.size + ar[-1].size > 6
  #=> 3 + 0 > 6 => false

所以我们执行:
ar[-1] += a
   #=> ["a", "b", "c"] 
ar #=> [["a", "b", "c"]]

下一个元素由枚举生成,传递给块并为块值分配值。
a, ar = enum.next
   #=> [["a", "b"], [["a", "b", "c"]]] 
a  #=> ["a", "b"] 
ar #=> [["a", "b", "c"]]

条件语句被评估。
a.size + ar[-1].size > 6
  #=> 2 + 3 > 6 => false

因此,我们再次执行:

ar[-1] += a
   #=> ["a", "b", "c", "a", "b"] 
ar #=> [["a", "b", "c", "a", "b"]]
< p > 枚举 然后将第三个元素传递给块。

a, ar = enum.next
   #=> [["a", "b", "c"], [["a", "b", "c", "a", "b"]]] 
a  #=> ["a", "b", "c"] 
ar #=> [["a", "b", "c", "a", "b"]] 

因为:

a.size + ar[-1].size > 6
  #=> 3 + 5 > 6 => false

这次我们执行的是:
ar << a
  #=> [["a", "b", "c", "a", "b"], ["a", "b", "c"]] 

剩下的步骤类似。

1
@Sergio,我无法从大师的眼神中逃脱。 - Cary Swoveland
[[]]: 真聪明! - iGian

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接