From my understanding of how folding works, given an array of integers and some existing value (say, 2)
val = 2
arr = [1,2,3]
I can say
arr.inject(val) do |r, val|
r += val
end
To add all of the elements to the specified value in some specific order (left to right?).
So now if I had instead an array of arrays of integers like this
val = 2
arr = [ [1], [2], [3] ]
My logic would be similar, except I now I have an extra layer of arrays:
val = arr.inject(val) do |r, arrElmt|
r = arrElmt.inject(r, :+)
end
But now I only want every other element starting with the first element, so I want to ignore the [2]
and just add up the 1 and 3.
I'm not sure how to do this with a fold so I end up going back to using something like each_with_index
and then skipping odd indices.
val = 2
arr = [ [1], [2], [3] ]
arr.each_with_index do |arrElmt, i|
next if i.odd?
val = arrElmt.inject(val, :+)
end
Is there a better way to write this?
If the inner arrays are just one element, you can flatten first. There are probably a dozen ways to do this whole thing in Ruby, many of them a little obtuse-looking, but here's another:
val += arr.flatten.values_at(* arr.each_index.select(&:even?)).reduce(:+)
This adds up the odd values and adds the result to val
. It might look incorrect at first, with the even?
method, but it's because Ruby arrays are 0
index based.
You could do something like this:
val = arr.each_slice(2).inject(val) do |total, (even, _odd)|
even.inject(total, :+)
end
each_slice(n)
yields items n at a time. In this case it will first yield first & second items, then third and fourth and so on. Then I use only the first element of each of these pairs in the sum. If you wanted to sum the odd indices then you'd just change pair[0]
to pair[1] || []
pair[1] || []
That said, you could probably argue that your original attempt was clearer.
Here's some fun with destructuring bind of parameters:
arr.each_slice(2).inject(val) {|acc, ((el, *_), _)| acc + el }
A more serious way would be:
arr.each_slice(2).inject(val) {|acc, (el, _)| acc.+(*el) }
or
arr.each_slice(2).inject(val) {|acc, (el, _)| acc + el.first }
Something closer to your original attempt:
arr.each_with_index.inject(val) {|acc, (el, i)|
if i.odd? then acc else acc + el.first end }
Another way:
def sum_even_elements(arr)
arr.each_slice(2).map(&:first).flatten.reduce(:+)
end
sum_even_elements([1,2,3,4,5]) #=> 9
sum_even_elements([[1],[2],[3],[4],[5]]) #=> 9
sum_even_elements([[1,2],[3,4],[5,6],[7,8],[9,0]]) #=> 23
If needed:
def sum_odd_elements(arr)
arr.flatten.reduce(:+) - sum_even_elements(arr)
end
Inefficient? Yes, but just a small cost to make it read well.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.