Live data from Hacker News

Ruby's hash is a Swiss-army knife

akshaykhot.com

41–50 of 81 posts

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

#41
The only thing I really miss in the default Ruby hash is property access for keys like JavaScript has.

I know, I know, OStruct does this but it's not the thing that gets built from hash literals so it's less convenient.

It's a minor quibble

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

#42

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...

Yes. The OP's post is basically "baby's first API." The are a very handy API but relying on them at scale is much less efficient than structs or arrays. This post doesn't even go into the different performance characters of these APIs, which can be a major foot gun. I've noticed a lot of novice-level posts on here lately. Although I admit the post was very well written.

Ruby structs or c-family structs?

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

#43

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…

There's #lazy to turn things into a lazy enumerator, to be iterated over when you so desire with e.g #force.

If you're going to iterate over an accumulator variable, use the for keyword instead of each, it's faster.

Alternatively, one can use .reduce({}) { |h, (k, v)| ... h } or .each.with_object({}) { |(k, v), h| ... } which makes the block not close over an external variable, and makes the assignment to that variable "atomic" (wrt the hash construction, the variable will only contain the final result, that is if a final variable is needed at all, which it may not with implicit return of the last value)

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

#44

Ruby’s Hash is probably the handiest data structure I’ve ever encountered. Thanks Matz.

I wish it was typed though. So many times I’ve seen a function that takes a hash of “options” or “config” and have no idea what that actually contains. Even for official rails methods it’s often complex to know what the possible options are. Some of them seem almost internal with how obscure they are.

RBS+steep to the rescue! We typed our configuration this way for ddtrace Ruby. On the external (set) side it makes it very easy to explore configuration in an editor, on the internal (get) side it makes us ensure we don't make mistakes.

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

#45
post #39

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

Let’s make it a one liner :D fib = Hash.new {|hash, key| hash[key] = key Example: fib[123] # => 22698374052006863956975682 Makes use of memoization.

Yes, I was going off of memory and thought that it was memoising it, but I made a mistake and fixed it.

  fib = Hash.new do |k, v|
    next 1 if v == 0 || v == 1
    k[v] = k[v-1] + k[v-2]
  end
I like yours better though, it seems a lot simpler with the less than 2 check.

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

#46

The only thing I really miss in the default Ruby hash is property access for keys like JavaScript has. I know, I know, OStruct does this but it's not the thing that gets built from hash literals so it's less convenient. It's a minor quibble

OpenStructs are ungodly slow anyway. If you want such accessors you probably want a plain Struct anyway and not mix hashness and method accessors. You can even define additional methods on structs:

    Foo = Struct.new(:a, :b, keyword_init: true) do
      def frobz
        ...
      end
    end
Structs are quicker to instantiate than classes but slower to call methods on, so if you instantiate a lot but seldom call they're great. Otherwise a class is better.

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

#47
post #37

It is just a hash map with a few common functions defined; hash maps are occasionally useful, but what is all the praise about? The mentioned "simple" bit is arguable: as a language's building block, a hash map is relatively complex and specific, since those can be built out of lists (or trees, though lists/arrays may be preferable for efficiency), which can be built out of pairs (product types, cons cells, tuples; u…

Well yeah, it's "simple" (or easy) to use, definitely not simple to implement. But having an easy-to-use hashmap is par for the course for most newer languages - not only Ruby, but also PHP (associative arrays), JS (objects), Go (built-in map type) etc.

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

#48

Also that it maintains insertion order (since Ruby 1.9) opens up more use cases.

Do you know any use cases, other than iteration? Would love to know any interesting ones. Thanks!

Yeah, you can use it to implement a simple (but not necessarily very performant) LRU: https://github.com/SamSaffron/lru_redux/blob/037ee594aded597...

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

#49
post #14

In my opinion Lua's equivalent tables or more of a Swiss army knife. It's been a while since I've used them but afair they combine hash/dict, array and objects in one data structure. They're hashes as usual. With a special constructor or int keys they turn into arrays, last I looked the Lua interpreter even optimizes contiguous ranges of keys as arrays. And by adding metatables and metamethods they also emulate JS-st…

My feeling with Lua is that it's lacking arrays due to minimalism, as in "we don't need arrays because tables will do". Probably the same reason why there is no "continue" in a for-loop. In comparison, Ruby has arrays and hashes, and gives me the feeling "yes these do exactly what you expected, but wait there is more!" In that sense it does feel more like a Swiss army knife.

On the contrary, they acknowledged that hash tables only was pushing it a bit too far, so they implemented arrays sort of "on the side" of tables.

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

#50
post #46

The only thing I really miss in the default Ruby hash is property access for keys like JavaScript has. I know, I know, OStruct does this but it's not the thing that gets built from hash literals so it's less convenient. It's a minor quibble

OpenStructs are ungodly slow anyway. If you want such accessors you probably want a plain Struct anyway and not mix hashness and method accessors. You can even define additional methods on structs: Foo = Struct.new(:a, :b, keyword_init: true) do def frobz ... end end Structs are quicker to instantiate than classes but slower to call methods on, so if you instantiate a lot but seldom call they're great. Otherwise a cl…

The new Data class in Ruby 3.2 also does the job (although it's intended for immutable data)

https://docs.ruby-lang.org/en/3.2/Data.html

Post reply on HN