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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.