user3619165
user3619165

Reputation: 408

Incrementation in Ruby hashes

I'm trying to increment the key in a hash. For example. I'm trying to get this

{:b => "crayons", :c => "colors", :d => "apples"} 

to turn into this

{:c => "crayons", :d => "colors", :e => "apples"} 

I thought this code would do the trick but it doesn't. What do I need to change?

def hash(correct)
  mapping = correct.each{|key, element| key.next}
  Hash[correct.map {|key, element| [mapping[key], element]}]
end

Upvotes: 3

Views: 145

Answers (5)

7stud
7stud

Reputation: 48599

I thought this code would do the trick but it doesn't.

mapping = correct.each{|key, element| key.next}

If you go to the ruby Symbol docs and click on the link for next()...surprise there is no entry for next, but the description at the top of the window says:

succ
Same as sym.to_s.succ.intern.

From that you have to deduce that next() is a synonym for succ(). So Symbol#next/succ converts the symbol to a string by calling to_s(). Well, you know that you are going to get a String returned from to_s, and no matter what you do to that String, e.g. calling String#succ on it, it isn't going to effect some Symbol, e.g. your hash key. Furthermore, if you look at the docs for String#succ, it says

succ -> new_string

...so String#succ creates another String object and calling intern() on that String object, and by the way intern() is just a synonym for to_sym(), once again won't affect some Symbol...and it won't even affect the String object returned by to_s.

Finally, intern() doesn't change the second string object but instead returns a Symbol:

                  a String
                    V
key.next => key.to_s.succ.intern => Symbol
                         ^
                       another String

...and because you didn't do anything with the Symbol returned by intern(), it is discarded.

Upvotes: 1

Cary Swoveland
Cary Swoveland

Reputation: 110675

h = {:b => "crayons", :c => "colors", :d => "apples"}

h.keys.map(&:succ).zip(h.values).to_h
  #=> {:c=>"crayons", :d=>"colors", :e=>"apples"}

If the intent were to modify (not keep) the original hash, the update could be done in place:

keys = h.keys.reverse
keys.each { |k| h[k.succ] = h[k] }
h.delete(keys.last)

which could be inscrutablized to:

h.delete(h.keys.reverse.each { |k| h[k.succ] = h[k] }.last)

Upvotes: 2

cvibha
cvibha

Reputation: 713

def hash(correct)
  exp_hash = correct.map { | k, v|  {k.next => v}  }
  Hash[*exp_hash.collect{|h| h.to_a}.flatten]
end

correct = {:b => "crayons", :c => "colors", :d => "apples"}

Upvotes: 1

bjhaid
bjhaid

Reputation: 9762

Using Enumerable#each_with_object

def hash_correct(hsh)
  hsh.each_with_object({}) { |(k,v), hsh| hsh[k.succ] = v }
end

hash_correct({:b => "crayons", :c => "colors", :d => "apples"})
# => {:c=>"crayons", :d=>"colors", :e=>"apples"}

Upvotes: 5

sawa
sawa

Reputation: 168091

def hash(correct)
  Hash[correct.map{|key, element| [key.next, element]}]
end

Upvotes: 4

Related Questions