Sort hash by length of contained values

Let's say I have a hash like:

foo = {
  :bar => ['r', 'baz'], # has a total str length of 4 characters inside of the array
  :baz => ['words', 'etc', 'long words'] # has a total str length of 18 characters inside of the array,
  :blah => ['at'] # has a total str length of 2 characters inside of the array
  # etc...
}

How can I sort this hash by the total length of the string of elements contained in arrays? The resulting hash order in this case should be::blah, :bar, :baz

+5
source share
4 answers

I would just do this:

Hash[foo.sort_by { |k, v| v.join.length }]

I assume that you do not intend to change the original Hash values, just reorder them.

+11
source

Traditionally, hashes are not ordered and therefore not sorted. Ruby 1.9 hashes are ordered, but the language does not provide an easy way to reorder items. As in 1.8, hash sorting returns an array of pairs:

{ c:3, a:1, b:2 }.sort => [ [:a,1], [:b,2], [:c,3] ]

(Actually, 1.8 would blow it up, because the characters are not comparable in 1.8, but it doesn't matter).

, ( ) . sort_by , , , :

foo.sort_by { |key, strings| strings.join.length }

, , :

foo.sort_by { |key, strings| -strings.join.length }

, 1.9 , (, Jörg W Mittag):

Hash[ foo.sort_by { |key, strings| strings.join.length } ]

..., , d11wtq.

+3
   foo.sort_by { |_,v| v.reduce(:+).size }
+1

Hash . Ruby 1.9 . , , 1.9.

But I do not want to recommend relying on this behavior because it is implicit behavior, and I am afraid of future changes. Instead, use a method that hashes the entry in the order of the sum of the length of the lines.

def each_by_length(hash)
  hash = hash.sort_by { |_, strs| strs.map(&:length).inject(0, &:+) }
  hash.each do |k, v|
    yield k, v
  end
end
+1
source

All Articles