Live data from Hacker News

Ruby's hash is a Swiss-army knife

akshaykhot.com

61–70 of 81 posts

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

#61

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]

No, because that is equivalent to

    k[v] || (k[v] = k[v-1] + k[v-2])
And that first k[v] (unlike k.key?(v)) will trigger the Hash.new block again, so it'll recurse until it runs out of stack. But neither check is necessary, because the Hash.new block will only ever get called if k.key?(v) is false.

If you want a more compact version, you could do:

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

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

#62

Earlier quoted context omitted.

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

I know it's been almost a year but I still haven't accepted this pattern.

Using errors as general flow control makes me uncomfortable. It shouldn't be an error or exception except in.... Actual problems.

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

#63
Hash is so powerful in Ruby that people often overuse them.

One of the most common issues I found on Ruby code-bases is to not create classes to represent their domain and simply use hashes everywhere.

The downside is that a hash has no shape. It can (and will) be anything you want it to be, often causing havoc once the system grows.

Checks for keys everywhere. Almost all statements use the safe navigation because you never know what shape you're dealing with. Multiple places performing the same map/reduce/filter/etc. All because people stick to hashes a bit too long.

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

#64
post #63

Hash is so powerful in Ruby that people often overuse them. One of the most common issues I found on Ruby code-bases is to not create classes to represent their domain and simply use hashes everywhere. The downside is that a hash has no shape. It can (and will) be anything you want it to be, often causing havoc once the system grows. Checks for keys everywhere. Almost all statements use the safe navigation because yo…

Ruby newly added type system can also help here. For starters, it'd be nice to know what type(s) the keys and values can be.

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

#65
post #58

Earlier quoted context omitted.

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.

[deleted]

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

#66
post #58

Earlier quoted context omitted.

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.

You don't need the `unless k.key? v` guard. The `Hash.new` block only gets called when the key is not present in the hash.

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

#67

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…

Unless things have changed and Ruby has stream fusion now, this is bad advice for scale. You are iterating over a fat object multiple times. Even if its uglier its much better in this case to create an empty array/hash, iterate over with #each and # I worked at the largest Rails shop in the world and this would be rejected in code review. Edited to add more detail: the only method you need to write to implement Enume…

> I worked at the largest Rails shop in the world and this would be rejected in code review.

Not sure if this means GitHub or Shopify. Until earlier this year I worked at GitHub for a decade, leaving as a principal engineer, primarily writing Ruby.

This would not be rejected at code review there unless the Hash had e.g. millions of values and, even then, it might not be a meaningful performance problem in context.

If the Hash is very small and will always be: readability trumps Big-O "performance" when n is very small.

Am I being pedantic and appealing to authority? Yup but, well, you started it and I hate to see helpful "Ruby is nice" comments like the grandparent get crapped on for no good reason.

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

#68

Earlier quoted context omitted.

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.

You don't need the `unless k.key? v` guard. The `Hash.new` block only gets called when the key is not present in the hash.

The caching makes it faster the trade off being more using more memory. I just wanted to see if it’d work.

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

#69
post #63

Hash is so powerful in Ruby that people often overuse them. One of the most common issues I found on Ruby code-bases is to not create classes to represent their domain and simply use hashes everywhere. The downside is that a hash has no shape. It can (and will) be anything you want it to be, often causing havoc once the system grows. Checks for keys everywhere. Almost all statements use the safe navigation because yo…

Amen. This is an issue at the company I work at. Common typos when looking up has keys will return nil - this has a tendency to silently keep working and blow up with a runtime error further down the chain. I am trying to insist on using .fetch to force an exception.

When the company switches to 3.2 I will insist on everyone using the new Data class for value objects rather than hashes.

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

#70
post #13

Earlier quoted context omitted.

Yes, hashes in Ruby, associative arrays in PHP, maps in Go[1], dictionaries in Python[2] and C#[3] represent the same concept, a collection of key-value pairs. [1]: https://gobyexample.com/maps [2]: https://docs.python.org/3/tutorial/datastructures.html#dicti... [3]: https://learn.microsoft.com/en-us/dotnet/api/system.collecti...

The interesting thing is that it definitely feels different to me. While the basic data structure is obviously identical to its counterparts, the way it interacts with the ecosystem as a whole makes it feel way more powerful. The interaction of hashes, symbols, and blocks lead to a language which feels like it is designed with DSLs as first-class citizens. This leads to things like Ruby on Rails, which at first glanc…

> The interaction of hashes, symbols, and blocks lead to a language which feels like it is designed with DSLs as first-class citizens.

Totally agree.

Post reply on HN