I tried searching for an answer but nothing I found has worked...
I have an array that looks like this... [["10", "20", "1"], ["10", "30", "1"], ["10", "30", "1"]]
and I'd like to group them by both the first and second elements.
Result [[["10", "20", "1", 1]], [["10", "30", "1", 2], ["10", "30", "1"]]]
From what I found I've tried these...
list.group_by{|w| w[3]}.values do |v1| v1.group_by{|l| l[4]} end
list.group_by{|w| w; [w[3], w[4]]}.map{|k, v| [k.first, k.last, v.length]}
This works but only takes one of the elements into account when grouping...
sub_group=list.group_by{|w| [w[3], w[4]]}.values
sub_group.each{|group| group[0] << group.length}
CodePudding user response:
You could group the array to get a count of elements, then map the values by checking their index, when it's 0 then you add the total elements;
[["10", "20", "1"], ["10", "30", "1"], ["10", "30", "1"]]
.group_by(&:itself)
.values
.map do |values|
values.map.with_index do |value, i|
next value [values.length] if i.zero?
value
end
end
# [[["10", "20", "1", 1]], [["10", "30", "1", 2], ["10", "30", "1"]]]
CodePudding user response:
With Enumerable#tally
and Enumerable#each_with_object
[["10", "20", "1"], ["10", "30", "1"], ["10", "30", "1"]]
.tally
.each_with_object([]) do |(item, count), new_ary|
new_item = item.dup
new_item << count
new_ary << [new_item]
(count - 1).times { new_ary.last << item }
end
# => [[["10", "20", "1", 1]], [["10", "30", "1", 2], ["10", "30", "1"]]]
You can also use Array#flatten!
instead first two lines inside each_with_object
. In this case in times
you need item[..-2]
[["10", "20", "1"], ["10", "30", "1"], ["10", "30", "1"]]
.tally
.each_with_object([]) do |item, new_ary|
item.flatten!
new_ary << [item]
(item.last - 1).times { new_ary.last << item[..-2] }
end
# => [[["10", "20", "1", 1]], [["10", "30", "1", 2], ["10", "30", "1"]]]