按子数组总和大小 Ruby 拆分子数组数组
Posted
技术标签:
【中文标题】按子数组总和大小 Ruby 拆分子数组数组【英文标题】:Split array of subarrays by subarrays sum size Ruby 【发布时间】:2020-11-30 10:05:16 【问题描述】:我有一个子数组:
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)。
【参考方案1】:
您可以使用 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 方法将通过迭代可枚举对象的每个元素将任何可枚举对象“减少”为单个值,就像each
、map
一样
考虑一个例子:
# 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
这里有一个很好的article 供进一步参考
如果你以你的问题为例:
# 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就不会产生副作用,直接返回最终的数组。
非常感谢,塞尔吉奥!我已经更新了代码,而是将其缩减为一个数组。 :) 希望更多地了解副作用。任何参考将不胜感激!
@SergioTulentsev 错过了提及您。 :P
啊,是的,这正是我的意思 :) “副作用”我的意思是你的 reduce 块正在改变外部状态(那个数组),而不是只使用累加器和当前元素。
知道了。 :) 非常感谢您的输入。干杯!【参考方案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([[]])>
第一个值由此枚举器生成,传递给块,并通过将Array Decomposition 应用于传递给块的二元素数组来为块值分配值。
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"]]
下一个元素由enum
生成,传递给块,块值被赋值。
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"]]
enum
然后将第三个元素传递给块。
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"]]
其余步骤类似。
【讨论】:
@Sergio,我无法从大师那里看到这种表情。[[]]
: 真聪明!以上是关于按子数组总和大小 Ruby 拆分子数组数组的主要内容,如果未能解决你的问题,请参考以下文章