2

How can I create a new array for summing array elements in place in Ruby?

[1,2,3,4,5].each_cons(2).map {|a, b| a + b }

gives me [3, 5, 7, 9] but expected result is [1,3,6,10,15].

6
  • ⇑ Welcome, PHP! Commented Nov 22, 2016 at 9:12
  • Yes, just old school solution.. :) Commented Nov 22, 2016 at 9:28
  • @MuratUstuntas what does "in place" mean? Commented Nov 22, 2016 at 11:40
  • @Stefan, i means "in place" that you should update the original array rather than creating a new one. Commented Nov 23, 2016 at 5:29
  • 1
    @MuratUstuntas in that case, "How can i create a new array" is a bit misleading. Commented Nov 23, 2016 at 9:33

5 Answers 5

8

More simple for understanding, I think:

temp_sum = 0
arr.map! {|e| temp_sum += e }
=> [1, 3, 6, 10, 15]

If you want to create a new array instead of existing one, just use map instead of map!

Sign up to request clarification or add additional context in comments.

6 Comments

The first answer here so far answering “in place” part of the question. It has a drawback of producing N sums, but it could be fixed with .with_object(temporary_sum).
@mudasobwa, fixed:)
arr.each_with_object(sum: 0).map! {|e, acc| acc[:sum] += e }
@mudasobwa, this is shining.
@SunnyMagadan, usually in place means mutator. But I'll add your remark.
|
2

Lots of ways to do this. One way is to create an Enumerator instance and use inject:

def adder array
  enum = Enumerator.new do |y|
    array.inject (0) do |sum, n|
      y << sum + n
      sum + n
    end
  end
  enum.take array.size
end

adder [1,2,3,4,5] #=> [1, 3, 6, 10, 15]

2 Comments

(sum + n).tap &y.method(:<<)
@mudasobwa, yes this is great idea.
1
[1, 2, 3, 4, 5].each_with_object([]){|e, a| a.push(a.last.to_i + e)}
# => [1, 3, 6, 10, 15]

5 Comments

to_i results in failing on, say, strings.
#to_i is certainly there for the convention that nil.to_i returns 0. Aimed at numbers only.
@mudasobwa Not sure what you mean. Where does a string come from?
Although this might solve the problem, it is always good to add an explanation why/how this works.
@sawa Array might consist of any objects, having #+ defined on, I don’t see any reason to reject anything save for Numerics. a.push(a.last ? a.last + e : e) would work for anything.
1

Another variation:

> [1, 2, 3, 4, 5].reduce([]) {|acc, el| acc << el + (acc[-1] || 0); acc}
#=> [1, 3, 6, 10, 15]

Yuk.

1 Comment

If you use inject/reduce and have to return an object in a second statement inside the block, it means you could probably use each_with_object.
0

It's not very elegant, but it seems to work :

[1,2,3,4,5].each_with_object([]){|i,l| l<<(l.last||0)+i}
#=> [1,3,6,10,15]

3 Comments

@MuratUstuntas this is insane (besides that this is not a winner, re-examine your benchmarks.) The difference in half of nanosecond should not matter when choosing the proper solution.
I agree with @mudasobwa : your benchmark basically shows that there' s no big statistical variations between those implementations. So choose only depending on, say, code readability and familiarity to the used methods.
Yes you are right.. I just say a word. If i made a mistake, forgive me. :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.