Live data from Hacker News

Ruby's hash is a Swiss-army knife

akshaykhot.com

51–60 of 81 posts

Re: Ruby's hash is a Swiss-army knife

#51

My favorite little-known fact about Ruby hashes is that they respond to `to_proc` and can be used as procs. For example, you can do this: a = { 1 => 'a', 2 => 'b' } [1, 2, 3].map(&a) #=> ['a', 'b', nil]

One of the most beautiful things in Ruby that I have ever seen is this fibonacci code. fib = Hash.new do |k, v| next 1 if v == 0 || v == 1 k[v-1] + k[v-2] end

With caching...

   fib = Hash.new do |k, v|
     next 1 if v == 0 || v == 1

     unless k.key? v
       k[v] = k[v-1] + k[v-2]
     end

     k[v]
   end

Re: Ruby's hash is a Swiss-army knife

#52

Earlier quoted context omitted.

One of the most beautiful things in Ruby that I have ever seen is this fibonacci code. fib = Hash.new do |k, v| next 1 if v == 0 || v == 1 k[v-1] + k[v-2] end

With caching... fib = Hash.new do |k, v| next 1 if v == 0 || v == 1 unless k.key? v k[v] = k[v-1] + k[v-2] end k[v] end

Wouldn't you get the same result using memoization idiomatic syntaxes?

k[v] ||= k[v-1] + k[v-2]

Re: Ruby's hash is a Swiss-army knife

#53

PHP's associative array is very similar yea?

Selling it as associative array is nice. (And I know the docs do that, too) It's a doubly linked list, array, hashmap all in one, with some weird magic when handling numeric indices. There's often the time when using another language that I made a bad choice by picking either an array or hash or list while later I need one of the other properties PHP would give me by default. The extra cost for most uses is neglectib…

> It's a doubly linked list, array, hashmap all in one, with some weird magic when handling numeric indices.

In what way, exactly, is PHP's associative array (a hash table of course, like all these things) a doubly linked list ?

Sure, in a language which isn't very fast anyway, I can believe it's reasonable to use your hashtable as an array, O(1)~ or O(1)* [Expected or Amortized constant time] probably feels enough like 1 [a single operation] in this language that you shouldn't worry about it. Explaining to beginners why they can't write digits["four"] is more trouble than it's worth, so how about just make it work instead.

It is a hashmap, so, that makes sense. These are different words for essentially the same type.

But how is it a doubly linked list ?

Re: Ruby's hash is a Swiss-army knife

#55

This is a lovely overview. Hash is a great example of how delightful it can be to program in Ruby. One more technique worth noting is the chained functional style of using Hash, which you can do in Ruby because Hash inherits from Enumerable. If you're prototyping a script to do some data-cleaning, this makes it easy to build up your pipeline and iterate on it. For example: foobar = { ...your data here... } foobar.map…

Yeah, that's a great demonstration of the technique.

One tiny tip.. :) you can pass a block to `.to_h`, so instead of using `.map` + `.to_h`:

    h.map { |k,v| [k, v] }.to_h
.. it can be simplified to:

    h.to_h { |k,v| [k, v] }

Re: Ruby's hash is a Swiss-army knife

#56

Earlier quoted context omitted.

I don't know ruby and if it's the same, but if you're not using a library like funcy or toolz for a nested get helper, you can do `dict.get('key', {}).get('key2', {}).get('key3')`. Not the prettiest, but can do in a pinch.

Thanks! i didn't know about these and will check them out for sure. Tired of `if key in hash:` nested layers.

In my opinion, a better alternative to nested `if key in hash`:

    try:
        value = data["foo"]["bar"]["baz"]
    except KeyError:
        value = None

Re: Ruby's hash is a Swiss-army knife

#57

My favorite little-known fact about Ruby hashes is that they respond to `to_proc` and can be used as procs. For example, you can do this: a = { 1 => 'a', 2 => 'b' } [1, 2, 3].map(&a) #=> ['a', 'b', nil]

One of the most beautiful things in Ruby that I have ever seen is this fibonacci code. fib = Hash.new do |k, v| next 1 if v == 0 || v == 1 k[v-1] + k[v-2] end

You can also recursively define fib as a lazy sequence that is the pairwise sum of fib and fib shifted by one. (Clojure, from Rosetta Code).

    (def fib (lazy-cat [0 1] (map + fib (rest fib))))

    => (take 10 fib)
    (0 1 1 2 3 5 8 13 21 34)


    Explanation:

       0 1 1 2 3 5   ;  this is fib
    +  1 1 2 3 5 8   ;  this is (rest fib) 
    ---------------
       1 2 3 5 8 13  ;  this is (map + fib (rest fib))
                     ;  and the sequence needs to be initialized with (lazy-cat [0 1] ...

Re: Ruby's hash is a Swiss-army knife

#58

Earlier quoted context omitted.

One of the most beautiful things in Ruby that I have ever seen is this fibonacci code. fib = Hash.new do |k, v| next 1 if v == 0 || v == 1 k[v-1] + k[v-2] end

With caching... fib = Hash.new do |k, v| next 1 if v == 0 || v == 1 unless k.key? v k[v] = k[v-1] + k[v-2] end k[v] end

But caching doesn't required here. Hash.new calls block only if value isn't initialialized.

Re: Ruby's hash is a Swiss-army knife

#59

Earlier quoted context omitted.

With caching... fib = Hash.new do |k, v| next 1 if v == 0 || v == 1 unless k.key? v k[v] = k[v-1] + k[v-2] end k[v] end

Wouldn't you get the same result using memoization idiomatic syntaxes? k[v] ||= k[v-1] + k[v-2]

That was the first thing I tried, but it blew the stack :) You can still blow the stack if you pick a number that's too high, like k[20000] or something. But if you pick a lower number & cache that, then you can (eventually) call a higher number without blowing the stack. This recursive approach is a horribly inefficient algorithm anyway, so I don't think it's worth optimizing :)

Re: Ruby's hash is a Swiss-army knife

#60
post #58

Earlier quoted context omitted.

With caching... fib = Hash.new do |k, v| next 1 if v == 0 || v == 1 unless k.key? v k[v] = k[v-1] + k[v-2] end k[v] end

But caching doesn't required here. Hash.new calls block only if value isn't initialialized.

I just did it for fun. This particular recursive approach is super slow for numbers of nontrivial size, so I was just curious if I could even make the caching work in the block. It's not worth optimizing a suboptimal query when a more efficient option is available anyway.
Post reply on HN