1

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}

5
  • Why there's no 2 in the second array from the second array in your result example? Commented Sep 21, 2022 at 16:45
  • @AmoskalescapingfromRussia I just need the length result to be in the first subarray... bu t the length is optional, I use this sub_group.each{|group| group[0] << group.length} to get the length of each nested array. Commented Sep 21, 2022 at 17:05
  • You need to edit to better state the question (forgetting, for now, what you've tried). Specifically, you need to state the rules that produce your stated "Result" for the example you have given. I can't figure it out and I'm probably not alone, especially because no answers have appeared nearly one hour after you posted the question. A more elaborate example and/or a second example would be helpful. Commented Sep 21, 2022 at 17:11
  • Tip: Use %w[ 10 20 1 ] as an alternative to the syntax-heavy [ "10", "20", "1" ]. Commented Sep 21, 2022 at 17:40
  • @CarySwoveland I added the bit of code that adds the length to the result array I posted, hopefully it adds clarification. Commented Sep 21, 2022 at 19:14

2 Answers 2

2

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"]]]
Sign up to request clarification or add additional context in comments.

2 Comments

You could just do group_by(...).values.map and skip the transform_values(...).values part.
beautiful beautiful
1

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"]]]

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.