Jay Fields: Array#collect_with_remaining
Jay Fields posted another interesting Ruby example and a call for alternate solutions. (Check his post out for problem details.) After some digging in the Ruby API docs and a lot of experimenting in tests and irb, here’s my offering:
def collect_with_remaining enum_with_index.inject([]) do |result, (element, index)| result << yield(element, values_at(*(0...size).to_a - [index])) endend
I took the opportunity to learn about #enum_with_index, particularly how it’s used in conjunction with #inject. I also learned how the asterisk prefix expands an Array into parameters — I’ve seen this before somewhere (Rails source?) but never really understood what it was doing. It came in handy here, because I had an array but #values_at only takes Ranges or indices as raw parameters.
So how this method works…
- In order to get the elements of the array other than “element” at “index”, we create a range of every index in the array, (0…size).
- We convert this range to an array (#to_a) and we remove the index of the current element ( – [index]). This gives us an array of every index but the current element’s.
- We send the elements in this array to #values_at, thanks to the aforementioned asterisk notation, and we get ba all of the elements except for the current one.
One thing I don’t like about this solution is that it creates the same range of indexes and converts it to the same array of indexes for each element. If I did some profiling and weren’t happy with performance, I would pull that code out of the block so that it’s only done once, and would only add one line of code doing so.
Other than that, I think this example really benefits from the elegance afforded by Ruby, not to mention the powerful features of some core APIs. It’s terse but doesn’t seem too obtuse or obscure; while it uses some relatively advanced Ruby, it shouldn’t be too hard to figure out.