1

I am trying to find out if it is possible to sort a Hash by a specific value if there are multiple values saved to a key.

Example Code:

{
key1: ["Value1", "Value2", "Value3"], 
key2: ["Value1", "Value2", "Value3"],
key3: ["Value1", "Value2", "Value3"]
}

I would like to be able to sort by the values in Value2 or by Value1 or any specific value.

If key1 and key2 have the same Value2 they will be returned as:

{Value2: [key1, key2]}

Example:

Value2 representes pets:

  • For all the Value2 that have dog, the key will be saved under a new key, dog.
  • For all the Value2 that have cat, it will gather all keys that have Value2 as cat and group it.
{cat: ["key1", "key2"], dog: ["key3"]}

I am working in Ruby and VScode. I do not want to use an array or nested array because this is not efficient.


Given

{
key1: ["Cop", "dog", "house"],
key2: ["doctor", "cat", "apartment"],
key3: ["Chef", "dog", "house"],
key4: ["Cop", "cat", "apartment"]
}

Expected Output if asked to sort by the values in value2

{
dog: [key1, key3],
cat: [key2, key4]
}
Christian
  • 4,902
  • 4
  • 24
  • 42
  • For example, "I have `[1,2,3]` and I want `[3,2,1]`." Like that. – Schwern Oct 18 '22 at 00:05
  • If it helps, pretend you're writing a test case for what you want. – Schwern Oct 18 '22 at 00:08
  • @Schwern If i have a bunch of keys set up like ```{key: [Value1, Value2, Value3]}```. I want to be able to look at all the Value2's. For all the keys that have ```dog``` as ```Value2```, group the keys together. For all the keys where ```value2``` is ```cat``` I want them grouped together. –  Oct 18 '22 at 00:10
  • 2
    Can you write that more like: "I have `{ key1: ["dog", "cat", "badger"], key2: ["parrot", "cat"], key3: ["dog", "parrot"] }` and I want `{ ...something... }` – Schwern Oct 18 '22 at 00:11
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/248868/discussion-between-spotomic-and-schwern). –  Oct 18 '22 at 00:19

3 Answers3

2

This doesn't appear to be about sorting, but rather grouping.

It's a matter of iterating through each pair and building a new hash of the matching keys and values.

# A Hash where the default value is a new empty array.
# See https://stackoverflow.com/questions/30367487/creating-a-hash-with-values-as-arrays-and-default-value-as-empty-array
grouped = Hash.new { |h, k| h[k] = [] }

# Iterate through the Hash
h.each { |key, things|
  # Get the 2nd thing
  thing = things[1]

  # Add its key to the thing's group
  grouped[thing] << key
}

p grouped
Schwern
  • 153,029
  • 25
  • 195
  • 336
1

Maybe not the optimal solution but it returns the expected result. The code is at least groups by value and includes the key, resulting in:

{"cat"=>[:key2, :key4], "dog"=>[:key1, :key3]}

hash = {
    key1: ["Cop", "dog", "house"],
    key2: ["doctor", "cat", "apartment"],
    key3: ["Chef", "dog", "house"],
    key4: ["Cop", "cat", "apartment"]
}

def group_by(animal, hash)
  keys = hash.select{|key, value| value[1] == animal }.keys
  {animal => keys}
end 

a = group_by("cat", hash)
b = group_by("dog", hash)
a.merge(b)

I am sure there is a more "direct" solution.

Christian
  • 4,902
  • 4
  • 24
  • 42
1

Using #each_with_object to build up a hash as we iterate over the keys in the original hash.

data = {
    key1: ["Cop", "dog", "house"],
    key2: ["doctor", "cat", "apartment"],
    key3: ["Chef", "dog", "house"],
    key4: ["Cop", "cat", "apartment"]
}

data.keys.each_with_object(Hash.new([])) { |k, h| 
  h[data[k][1]] += [k] 
}
# => {"dog"=>[:key1, :key3], "cat"=>[:key2, :key4]}
Chris
  • 26,361
  • 5
  • 21
  • 42