1

I have a hash like:

dict = { 
  "someKey"        => [ ... ],
  "anotherKey"     => [ ... ],
  "yetAnōtherKéy"  => [ ... ]
}

I want a new hash by sorting the original. Sorting should ignore the accents (done by replacing the accented characters with their un-accented version),

replacements = [
    ["ā", "a"], ["á", "a"], ["à", "a"], ["ǎ", "a"],
    ["ō", "o"], ["ó", "o"], ["ò", "o"], ["ǒ", "o"],
    ["ī", "i"], ["í", "i"], ["ì", "i"], ["ǐ", "i"],     
    ["ē", "e"], ["é", "e"], ["è", "e"], ["ě", "e"],
    ["ū", "u"], ["ú", "u"], ["ù", "u"], ["ǔ", "u"]
]

but the keys in the resulting hash should keep the original keys. How is that possible?


I tried

dict = Hash[dict.sort_by{|k,v| k}]

This works and does sort the hash. However, it doesn't ignore the accents, i.e., the words starting with an accented character go to the bottom.

Another attempt is:

replacements.each {|replacement| z.gsub!(replacement[0], replacement[1])}
sawa
  • 165,429
  • 45
  • 277
  • 381
Dr.Kameleon
  • 22,532
  • 20
  • 115
  • 223
  • @cremno Well... that's what I mean I'm not used to Ruby... lol. Just fixed it. :) – Dr.Kameleon Oct 12 '15 at 13:52
  • @Dr.Kameleon please show a little more context around the `replacements.each` line, it's hard to guess with that line alone. – Tamer Shlash Oct 12 '15 at 13:56
  • Possible duplicate of [Ruby method to remove accents from UTF-8 international characters](http://stackoverflow.com/questions/15686752/ruby-method-to-remove-accents-from-utf-8-international-characters) – zwippie Oct 12 '15 at 14:03
  • 2
    This is not a duplicate since he's asking for an answer in plain Ruby, not in Rails. – Tamer Shlash Oct 12 '15 at 14:06
  • Your code assigning the result to `dict` implies that you want the new hash under the same name as the original. If this is the case, it contradicts your description that you wanted a new hash. You should rather replace the old one. – sawa Oct 12 '15 at 14:18
  • 1
    @TamerShlash Read the related question, it's about plain Ruby too, although the accepted answer uses the Rails related i18n gem. But there are other answers too. In my opinion these questions are duplicates. – zwippie Oct 12 '15 at 14:24

3 Answers3

3

Code corrected following Cary Swoveland's comment.

replacements = Hash.new{|_, k| k}.merge(replacements.to_h)
dict.sort_by{|k,_| k.gsub(/./, replacements)}.to_h
sawa
  • 165,429
  • 45
  • 277
  • 381
  • 1
    Shouldn't that be `{ |k,_| k.gsub(/./,replacements) }`? – Cary Swoveland Oct 12 '15 at 15:22
  • @CarySwoveland Exactly. My shame. – sawa Oct 12 '15 at 15:25
  • 1
    My guess is you were thinking that initially, which is why you constructed the hash that way, then a funny thing happened on your way to the Forum. btw, it seems that the expression "my shame" is has been pushed aside by "my bad". – Cary Swoveland Oct 12 '15 at 15:51
  • 2
    You can also build a hash without default value, i.e. `replacements = replacements.to_h` and substitute the characters via `gsub(/[#{replacements.keys.join}]/, replacements)` – Stefan Oct 12 '15 at 16:08
  • One could also construct the hash thus: `h = replacements.to_h.tap { |h| h.default_proc = ->(h,k) {k} }`. – Cary Swoveland Oct 12 '15 at 17:37
1

Use the unidecoder gem.

require 'unidecoder'
Hash[dict.sort_by{|k,v| k.to_ascii }]
sawa
  • 165,429
  • 45
  • 277
  • 381
Dr.Kameleon
  • 22,532
  • 20
  • 115
  • 223
0

Override the sort method by passing in a block in which you first perform your substitutions gsub (if necessary) and then do your comparison <=>. The sort will return the original items of the enum sorted. A solution I arrived at by reading the documentation ri Hash.sort.

wurde
  • 2,487
  • 2
  • 20
  • 39