ovhaag - 1 year ago 72

Ruby Question

I try to clean my Code. The first Version uses

`each_with_index`

`Enumerable.inject_with_index-construct`

It works now, but seems to me as obscure as the first code.

Add even worse I don't understand the brackets around element,index in

`.. .inject(groups) do |group_container, (element,index)|`

but they are necessary

- What is the use of these brackets?
- How can I make the code clear and readable?

`class Array`

# splits as good as possible to groups of same size

# elements are sorted. I.e. low elements go to the first group,

# and high elements to the last group

#

# the default for number_of_groups is 4

# because the intended use case is

# splitting statistic data in 4 quartiles

#

# a = [1, 8, 7, 5, 4, 2, 3, 8]

# a.sorted_in_groups(3) # => [[1, 2, 3], [4, 5, 7], [8, 8]]

#

# b = [[7, 8, 9], [4, 5, 7], [2, 8]]

# b.sorted_in_groups(2) {|sub_ary| sub_ary.sum } # => [ [[2, 8], [4, 5, 7]], [[7, 8, 9]] ]

def sorted_in_groups(number_of_groups = 4)

groups = Array.new(number_of_groups) { Array.new }

return groups if size == 0

average_group_size = size.to_f / number_of_groups.to_f

sorted = block_given? ? self.sort_by {|element| yield(element)} : self.sort

sorted.each_with_index do |element, index|

group_number = (index.to_f / average_group_size).floor

groups[group_number] << element

end

groups

end

end

`class Array`

def sorted_in_groups(number_of_groups = 4)

groups = Array.new(number_of_groups) { Array.new }

return groups if size == 0

average_group_size = size.to_f / number_of_groups.to_f

sorted = block_given? ? self.sort_by {|element| yield(element)} : self.sort

sorted.each_with_index.inject(groups) do |group_container, (element,index)|

group_number = (index.to_f / average_group_size).floor

group_container[group_number] << element

group_container

end

end

end

Answer Source

What is the use of these brackets?

It's a very nice feature of ruby. I call it "destructuring array assignment", but it probably has an official name too.

Here's how it works. Let's say you have an array

```
arr = [1, 2, 3]
```

Then you assign this array to a list of names, like this:

```
a, b, c = arr
a # => 1
b # => 2
c # => 3
```

You see, the array was "destructured" into its individual elements. Now, to the `each_with_index`

. As you know, it's like a regular `each`

, but also returns an index. `inject`

doesn't care about all this, it takes input elements and passes them to its block as is. If input element is an array (elem/index pair from `each_with_index`

), then we can either take it apart in the block body

```
sorted.each_with_index.inject(groups) do |group_container, pair|
element, index = pair
# or
# element = pair[0]
# index = pair[1]
# rest of your code
end
```

Or destructure that array right in the block signature. Parentheses there are necessary to give ruby a hint that this is a single parameter that needs to be split in several.

Hope this helps.