4

I have an array of arrays with x and y values:

[[some_date1, 1], [some_date2, 3], [some_date3, 5], [some_date4, 7]]

The result should only sum the y values (1, 3, 5, 7) so that the result is like this:

[[some_date1, 1], [some_date2, 4], [some_date3, 9], [some_date4, 16]]

How is this possible in Ruby?

Tomanow
  • 7,247
  • 3
  • 24
  • 52
  • 2
    What have you tried? It's important to include your attempt to find a solution in your question. That way we can tweak your code rather than supply something that has no relationship to what you've done, forcing you to shoehorn it into place, which can induce bugs accidently. – the Tin Man May 02 '13 at 14:05
  • You should avoid using a list with fixed position for things - most likely would be better use a hash instead, or else your program will be as hard to follow as NET::HTTP lib – fotanus May 02 '13 at 14:34
  • Your question has nothing to do with time series. It is a bad habit to put irrelevant information in the question and the title. You could have used `:foo`, etc instead of `some_date1`, etc (but it might be too late fix that now as some answers are based on that). As the instruction to this site says, make the question as general as possible. – sawa May 02 '13 at 14:49
  • The data is for a chart that plots the data over time but it does not work as I expected. Somehow the values go down in some instances, but theoretically since it is cumulative, it should only go up. I am thinking the problem lies within the way the data is combined with multiple entries for certain dates. I am thus thinking that I need to sum each day before accumulating. However, this is not relevant to the question I asked and will not influence my answer choice. – Tomanow May 02 '13 at 14:56

5 Answers5

3

Yes, this is possible in Ruby. You can use [map][1] and do something like this:

sum = 0
array.map {|x,y| [x, (sum+=y)]}

This is how it works. For the given the input:

array = ["one", 1], ["two", 2]

It will iterate through each of the elements in the array e.g.) the first element would be ["one", 1].

It will then take that element (which is an array itself) and assign the variable x to the first element in that array e.g.) "one" and y to the second e.g.) 1.

Finally, it will return an array with the result like this:

=> ["one", 1], ["two", 3]
Scott Bartell
  • 2,801
  • 3
  • 24
  • 36
3

You can use map:

a = [[:some_date1, 1], [:some_date2, 3], [:some_date3, 5], [:some_date4, 7]]

sum = 0
a.map { |f, v| [f, (sum = sum + v)]}

=> [[:some_date1, 1], [:some_date2, 4], [:some_date3, 9], [:some_date4, 16]]

Since sum will be nil in the first iteration it is necessary to call to_i on it.

jbr
  • 6,198
  • 3
  • 30
  • 42
2
a = [['some_date1', 1], ['some_date2', 3], ['some_date3', 5], ['some_date4', 7]]
a.each_cons(2){|a1, a2| a2[1] += a1[1]}
sawa
  • 165,429
  • 45
  • 277
  • 381
1
last = 0
arr.map do |a, b|
  last = last + b
  [a, last]
end
azgult
  • 542
  • 3
  • 10
1

I'd use:

ary = [['some_date1', 1], ['some_date2', 3], ['some_date3', 5], ['some_date4', 7]]
ary.inject(0) { |m, a| 
  m += a[-1]
  a[-1] = m
}

After running, ary is:

[["some_date1", 1], ["some_date2", 4], ["some_date3", 9], ["some_date4", 16]]

The reason I prefer this is it doesn't require the addition of an accumulator variable. inject returns a value but it gets thrown away without an assignment.

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
  • This seems scary to me (though pretty cool that it works). I don't generally expect `inject` to modify the caller. – spike May 02 '13 at 14:18
  • It's not really, it's passing the arrays in by reference, which gives me access to their values. The advantage over using `map` is it doesn't require an accumulator value outside the loop. – the Tin Man May 02 '13 at 14:26
  • Is this a common use of inject (modifying the calling array)? My understanding is that it's bad practice to modify the object you're iterating over. Sorry if this is a little off topic. (related: https://groups.google.com/forum/?fromgroups=#!topic/ruby-core-google/wYAJdScsv9w) – spike May 02 '13 at 14:57
  • There are numerous ways of iterating over something and modifying it. The only time I'd worry about it is when the iteration will change as a result of the modification. Ruby's Array has `map!` which is used the same way, to modify in place, but that would require an external accumulator, leaking that into the outer variable space. – the Tin Man May 02 '13 at 15:01
  • Well that's a `!` function, which I'd expect to do some modifying in place. If there were an inject! function I'd have no problem with this. – spike May 02 '13 at 15:04
  • In the link you referenced, the issue is appending to the array, causing an infinite loop. That is different than modifying existing values which won't cause the array size to change. And, though there is `map!`, you're not concerned because other answers used `map` to accomplish the same thing? – the Tin Man May 02 '13 at 15:06
  • The map answer doesn't modify the source array, it returns a new one. My concern is with the readability of code that uses inject to modify the calling array. (the link wasn't to try to argue your code is somehow dangerous, just that it's using a non-! method in a non-idiomatic way). – spike May 03 '13 at 02:31