home :: computers :: programming :: ruby :: array to hash one liner

Array to Hash One-Liner

Programming in Ruby, I’ve badly missed Perl’s list syntax, which, among other things, makes converting between arrays and hashes really easy. In Ruby I have forever been converting an array to a hash like this:

a = [ 1, 2, 3, 4, 5 ]
h = {}
a.each { |v| h[v] = v }

Of course, this is anything bug concise. In Perl, I can just do this:

my @a = (1, 2, 3, 4, 5, 6);
my %h = map { $_ => $_ } @a;

Easy, huh? Well, I finally got fed up with the nasty hack in Ruby, did a little Googling, and figured out a way to do it in a single line:

a = [ 1, 2, 3, 4, 5, 6 ]
h = Hash[ *a.collect { |v| [ v, v ] }.flatten ]

Not quite as consice as the Perl version, and I have to construct a bunch of arrays that I then throw away with the call to flatten, but at least it’s concise and, I think, clearer what it’s doing. So I think I’ll go with that.

Comments & Trackbacks

Marshall wrote:

Looks like a job for inject. And a mixin. Woo!

module Enumerable
  def to_hash
    self.inject({}) { |h, i| h[i] = i; h }
  end
end
>> [1, 2, 3, 4].to_hash
=> {1=>1, 2=>2, 3=>3, 4=>4}
>> { :foo => :bar, :baz => :bliffl }.to_hash
=> {:foo=>:bar, :baz=>:bliffl}

Marshall wrote:

To be honest, I have no idea why that works for hashes. Specifically, I was surprised to find that the h[i] = i bit worked on them... what is i? In an array, it's the element. In a hash, it's somehow both the key and the value. Anyone know what's up?

brian d foy wrote:

Well, in Perl it's even easier: @hash{@a} = @a

Theory wrote:

Marshall: Hash defines its own to_hash method, so the mixin is never accessed.

brian: I'm obviously out of practice. :-(

—Theory

Aristotle Pagaltzis wrote:

brian: that's not an inlinable into the declaration.

Marshall: monkeypatching is something to ashamed of, not something to show off proudly. It is a strategy of last resort. Last resort.

Theory wrote:

Aristotle: For better or for worse, such monkey patching is de rigueur in the Ruby community. It has taken quite a while for me as Perl hacker to get used to it (whether I really like it or not is quite another question).

—Theory

darth wrote:

if you only care to test for the existence of the key, do

@hash{@a} = (undef) x @a;

and then test using the "exists" function. can save memory if the array is large and exists is slightly faster

rjbs wrote:

monkeypatching

What I've often wanted from Ruby is some form of lexical constant class redefinition. Example:

HashClass = RJBSHash

dict = { ... } # I just made an RJBSHash

Fat chance.

Piers Cawley wrote:

Personally, I'd use a set:

require 'set'
set = some_array.to_set

It covers most cases where you'd want to be converting to a hash. Otherwise, either an inject or, if you're going to be using the same conversion block lots of times, roll a new class whose initialize method does the right thing.

Theory wrote:

Piers: I always forget about inject:

h = a.inject({}) {|h, v| h.merge! v => v }

I like it! Shorter and probably a bit more efficient than my original solution. Although I suspect that, like in Perl, hashes in Ruby are more expensive than arrays. In that case, I'd do:

h = a.inject({}) {|h, v| h[v] = v; h }

Shorter, too.

Oh, and while I agree with you on sets, I specifically needed to create a lookup table mapping ActiveRecord IDs to objects. I just put a general example here. But I should keep sets in mind, too. And yeah, I was just using a hash as a data structure, I'd find myself looking at using real objects in a hurry.

—Theory

Aristotle Pagaltzis wrote:

David:

I know the Rubyists consider monkeypatching chic. However much they like it doesn’t legitimise it, though. One day they’ll wake up and notice that their piles of “clever” Ruby are just as nasty to maintain as they always claim that Perl code is. In both cases the problem is the same: the coder, not the language.

(In fact, I’ve only recently realised that the Perl community is just as pubescent in dragging out the “Perl has no infatuation with enforced privacy” chestnut. Ever since, I started writing private methods in my classes as anonymous functions stored in lexicals. Thankfully it is possible to avoid the language deficiency using another language feature.)

Piers Cawley wrote:

One of the many fine things about Smalltalk is that it's possible to monkeypatch with abandon (ferinstance, as soon as you add an isWhatever method to replace an isKindOf: Whatever call, you're going to have to add an isWhatever definition to Object too (returning false, obviously). If that's wrong, then I don't want to be right.

The thing that's great about Smalltalk monkeypatching is that you can also run code in a workstation that walks the tree of subclasses of Whatever, programmatically adding 'isSubclassName' methods to those subclasses (and to Object). But, when you've finished, you can browse the classes you monkeypatched, and find editable method definitions for each new method, just as if you'd added them to the classes by hand. Which means you get to throw away the (potentially) tricky to understand workspace script.

Theory wrote:

Aristotle,

I think that there is already some concern about the amount of monkey patching that Rails does. It really is quite insane. What's happening is that a lot of developers are becoming very dependent on programming Rails, rather than Ruby. For myself, as a grouchy old Perl hacker, I tend to write subclasses (say what? say Rails folks), and monkey patches to fix bugs that haven't yet been fixed in Rails itself.

Great discussion, people—thanks!

Powered by KinoSearch