JCLL
JCLL

Reputation: 5545

Ruby 'group_by'-like method for hash

I need a Ruby function that takes a hash as input (keys are Symbols and values are Arrays of Numerics) and returns the list of keys whose associated Arrays have the same first value.

Here is an example :

h={
  :k1 => [2,3,5,12],
  :k2 => [9,5,6,10],
  :k3 => [2,4,5, 8],
}

f(h) # should return [[:k1,:k3]] 

...because 2 appears as the same value in the two arrays associated with :k1 and :k3. The return Object is an array of array (because, several groups of keys can have the same array first values).

So far, I just grouped the arrays themselves :

def f(h)
  h.values.group_by{|ary| ary.first}
end   

# returns {2=>[[2, 3, 5, 12], [2, 4, 5, 8]], 9=>[[9, 5, 6, 10]]}

Upvotes: 0

Views: 310

Answers (4)

Ganesh Chandra
Ganesh Chandra

Reputation: 1

This is for list of array.
array1 = [1,2,3,4,5,6,7,8,9,10,11,12,13]
myarray = []
myfinalarray = []
array1.each do |item|
    if myarray.size == 3
        myfinalarray.push(myarray)
        myarray = []
    end 
    myarray.push(item)
end 
myfinalarray.push(myarray)
puts "group of 3 items of an array #{myfinalarray}"

Upvotes: -2

Julian Kniephoff
Julian Kniephoff

Reputation: 1023

Why not just use group_by as the title of your question suggests:

h2 = h.group_by { |k, v| v.first }

This gives you something like this:

{2=>[[:k1, [2, 3, 5, 12]], [:k3, [2, 4, 5, 8]]], 9=>[[:k2, [9, 5, 6, 10]]]}

You can then use this for further processing. Just want the keys?

h3 = h2.values.map { |v| v.map(&:first) }

Only want those with more than one key?

h3.reject { |v| v.length < 2 }

Or on h2 directly:

h2.reject { |k, v| v.length < 2 }

Upvotes: 2

Ilija Eftimov
Ilija Eftimov

Reputation: 820

I came up with this ugly thing:

h.inject({}) do |memo, (key, array)|
  memo[array[0]] ||= []
  memo[array[0]] << key
  memo
end.detect do |(key, values)|
  values.size > 1
end.last

It basically remaps the hash in the form of:

{first_item_in_array => keys_that_contain_it}

Or, in our case:

{2=>[:k1, :k3]}

Then it just detects the first pair that has more than one match and returns it:

[:k1, :k3]

Hope that helps!

Upvotes: 1

Lukas Baliak
Lukas Baliak

Reputation: 2869

Maybe you can use something like this

def get_same_first_value(h)
  h.each_with_object(Hash.new { |k, v| k[v] = [] }) do |(sym, arr), exp|
    exp[arr.min] << sym
  end.select { |k, v| v.size != 1 }.values
end

h = {
  :k1 => [2, 3, 5, 12],
  :k2 => [9, 5, 6, 10],
  :k3 => [2, 4, 5, 8],
}

get_same_first_value(h)
# => [[:k1, :k3]]

Upvotes: 0

Related Questions