How can I interweave items from two arrays? - ruby

How can I go from this:
for number in [1,2] do
puts 1+number
puts 2+number
puts 3+number
end
which will return 2,3,4 then 3,4,5 -> 2,3,4,3,4,5. This is just an example, and clearly not the real use.
Instead, I would like it to return 2,3 3,4 4,5 -> 2,3,3,4,4,5. I would like each of the puts to be iterated for each of the possible values of number; In this case 1 and 2 are the two possible values of 'number', before moving on to the next puts.
One way to do this is to create two lists, [2,3,4] and [3,4,5] and then use the zip method to combine them like [2,3,4].zip([3,4,5]) -> [2,3,3,4,4,5].

zip is good. You should also look at each_cons:
1.9.2p290 :006 > [2,3,4].each_cons(2).to_a
=> [[2, 3], [3, 4]]
1.9.2p290 :007 > [2,3,4,5,6].each_cons(2).to_a
=> [[2, 3], [3, 4], [4, 5], [5, 6]]
1.9.2p290 :008 > [2,3,4,5,6].each_cons(3).to_a
=> [[2, 3, 4], [3, 4, 5], [4, 5, 6]]
Because each_cons returns an Enumerator, you can use a block with it, as mentioned in the documentation for it, or convert it to an array using to_a like I did above. That returns the array of arrays, which can be flattened to get a single array:
[2,3,4,5].each_cons(2).to_a.flatten
=> [2, 3, 3, 4, 4, 5]
From the ri docs:
Iterates the given block for each array of consecutive elements. If no
block is given, returns an enumerator.
e.g.:
(1..10).each_cons(3) {|a| p a}
# outputs below
[1, 2, 3]
[2, 3, 4]
[3, 4, 5]
[4, 5, 6]
[5, 6, 7]
[6, 7, 8]
[7, 8, 9]
[8, 9, 10]

Maybe not the most readable code but you could use inject on the first range to create an array based on the summed up second range.
(1..3).inject([]){|m,n| (1..2).each{|i| m<<n+i }; m }
=> [2, 3, 3, 4, 4, 5]
This might be a little more readable
res=[]
(1..3).each{|r1| (1..2).each{|r2| res<<r1+r2 } }

[1, 2, 3].each { |i| [1, 2].each { |y| puts i + y } }

Related

How to access a nested element, passing array with coordinates

Is there any short way to access an element of a nested array, passing the array with coordinates? I mean something like:
matrix = [[1,2,3,4],[5,6,7,8]]
array = [1,1]
matrix [array]
# => 6
I just wonder if there is a shorter version than:
matrix [array[0]][array[1]]
I believe you want to use the Matrix class:
require 'matrix'
arr = [[1,2,3,4],[5,6,7,8]]
matrix = Matrix[*arr] #=> Matrix[[1, 2, 3, 4], [5, 6, 7, 8]]
matrix[1,1] #=> 6
matrix.row(1) #=> Vector[5, 6, 7, 8]
c = matrix.column(1) #=> Vector[2, 6]
c.to_a #=> [2, 6]
m = matrix.transpose #=> Matrix[[1, 5], [2, 6], [3, 7], [4, 8]]
m.to_a #=> [[1, 5], [2, 6], [3, 7], [4, 8]]
array.inject(matrix, :fetch)
# => 6
matrix[1][1]
should equal 6. matrix[1] is the 2nd array, matrix[1][1] is the second element in that array.

Ruby, perform operation on an array and return the new array, aswell as "changes"

I am looking for a way to perform a certain operation (for instance delete_if) on an array and return both the deleted elements, and the remaining elements.
For example
a = [1,2,3,4,5,6,7,8,9,10]
a.delete_if {|x| x.even? } #=> [[1, 3, 5, 7, 9]]
But what I am looking for is something like
a = [1,2,3,4,5,6,7,8,9,10]
a.some_operation #=> [[1,3,5,7,9],[2,4,6,8,10]]
How would I go about doing this?
Using Enumerable#partition:
a = [1,2,3,4,5,6,7,8,9,10]
a.partition &:even?
# => [[2, 4, 6, 8, 10], [1, 3, 5, 7, 9]]
The first element of the Enumerable#partition return value contains the elements that are evaluated to true in the block. So you need to use odd? to get what you want.
a.partition &:odd?
# => [[1, 3, 5, 7, 9], [2, 4, 6, 8, 10]]
You might be looking for something like this:
a = [1,2,3,4,5,6,7,8,9,10]
a.group_by { |x| x.even? }.values
#=> [[1, 3, 5, 7, 9], [2, 4, 6, 8, 10]]

Sum arrays by index using functional programming

I have several equally sized arrays containing numbers (matrix), and I want to sum them all by their index (matrix columns).
For example, if I have:
data = [[1, 2, 3, 4], [5, 6, 7, 8]]
I want to get the result:
column_totals = [6, 8, 10, 12]
I understand how to do this imperatively, but how would I do this using functional programming? (Preferably, using built in Enumerable methods.) I wasn't very happy with any of the functional solutions I came up with.
I ended up using the Matrix class:
require 'matrix'
data = [[1, 2, 3, 4], [5, 6, 7, 8]]
matrix = Matrix[*data]
# Added sum method to Vector class.
matrix.column_vectors.map { |column| column.sum }
I'm happy enough with that solution, but am frustrated that I couldn't wrap my mind around a good functional solution without relying on the Matrix class.
Specifically, I was tripped up on the step to transform this:
data = [[1, 2, 3, 4], [5, 6, 7, 8]]
into this:
columns = [[1, 5], [2, 6], [3, 7], [4, 8]]
Any reason to not use Array#transpose?
data.transpose
# => [[1, 5], [2, 6], [3, 7], [4, 8]]
Alternatively, if you only want to use Enumerable methods to iterate, you can do
columns = data.inject(Array.new(data.first.length){[]}) { |matrix,row|
row.each_with_index { |e,i| matrix[i] << e }
matrix }
# => [[1, 5], [2, 6], [3, 7], [4, 8]]
or
columns = data.flatten.group_by.with_index { |e,i| i % data[0].size }.values
# => [[1, 5], [2, 6], [3, 7], [4, 8]]
To sum:
columns.map { |row| row.inject :+ }
# => [6, 8, 10, 12]
Thirdly, if you don't need the intermediate columns:
data.inject { |s,r| s.zip(r).map { |p| p.inject :+ } }
# => [6, 8, 10, 12]
You could use Array#transpose, as #Matt hinted, and then sum the arrays inside:
data.transpose.map {|a| a.reduce(:+) }

How to make an array containing a duplicate of an array

I couldn't find a way to build an array such as
[ [1,2,3] , [1,2,3] , [1,2,3] , [1,2,3] , [1,2,3] ]
given [1,2,3] and the number 5. I guess there are some kind of operators on arrays, such as product of mult, but none in the doc does it. Please tell me. I missed something very simple.
Array.new(5, [1, 2, 3]) or Array.new(5) { [1, 2, 3] }
Array.new(size, default_object) creates an array with an initial size, filled with the default object you specify. Keep in mind that if you mutate any of the nested arrays, you'll mutate all of them, since each element is a reference to the same object.
array = Array.new(5, [1, 2, 3])
array.first << 4
array # => [[1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4]]
Array.new(size) { default_object } lets you create an array with separate objects.
array = Array.new(5) { [1, 2, 3] }
array.first << 4
array #=> [[1, 2, 3, 4], [1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3]]
Look up at the very top of the page you linked to, under the section entitled "Creating Arrays" for some more ways to create arrays.
Why not just use:
[[1, 2, 3]] * 5
# => [[1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3]]
The documentation for Array.* says:
...returns a new array built by concatenating the int copies of self.
Typically we'd want to create a repeating single-level array:
[1, 2, 3] * 2
# => [1, 2, 3, 1, 2, 3]
but there's nothing to say we can't use a sub-array like I did above.
It looks like mutating one of the subarrays mutates all of them, but that may be what someone wants.
It's like Array.new(5, [1,2,3]):
foo = [[1, 2, 3]] * 2
foo[0][0] = 4
foo # => [[4, 2, 3], [4, 2, 3]]
foo = Array.new(2, [1,2,3])
foo[0][0] = 4
foo # => [[4, 2, 3], [4, 2, 3]]
A work-around, if that's not the behavior wanted is:
foo = ([[1, 2, 3]] * 2).map { |a| [*a] }
foo[0][0] = 4
foo # => [[4, 2, 3], [1, 2, 3]]
But, at that point it's not as convenient, so I'd use the default Array.new(n) {…} behavior.

Merge N sorted arrays in ruby lazily

How does one merge N sorted arrays (or other list-like data structures) lazily in Ruby? For example, in Python you would use heapq.merge. There must be something like this built into Ruby, right?
Here's a (slightly golfed) solution that should work on arrays of any 'list-like' collections that support #first, #shift, and #empty?. Note that it is destructive - each call to lazymerge removes one item from one collection.
def minheap a,i
r=(l=2*(m=i)+1)+1 #get l,r index
m = l if l< a.size and a[l].first < a[m].first
m = r if r< a.size and a[r].first < a[m].first
(a[i],a[m]=a[m],a[i];minheap(a,m)) if (m!=i)
end
def lazymerge a
(a.size/2).downto(1){|i|minheap(a,i)}
r = a[0].shift
a[0]=a.pop if a[0].empty?
return r
end
p arrs = [ [1,2,3], [2,4,5], [4,5,6],[3,4,5]]
v=true
puts "Extracted #{v=lazymerge (arrs)}. Arr= #{arrs.inspect}" while v
Output:
[[1, 2, 3], [2, 4, 5], [4, 5, 6], [3, 4, 5]]
Extracted 1. Arr= [[2, 3], [2, 4, 5], [4, 5, 6], [3, 4, 5]]
Extracted 2. Arr= [[3], [2, 4, 5], [4, 5, 6], [3, 4, 5]]
Extracted 2. Arr= [[4, 5], [3], [4, 5, 6], [3, 4, 5]]
Extracted 3. Arr= [[4, 5], [3, 4, 5], [4, 5, 6]]
Extracted 3. Arr= [[4, 5], [4, 5], [4, 5, 6]]
Extracted 4. Arr= [[5], [4, 5], [4, 5, 6]]
Extracted 4. Arr= [[5], [5], [4, 5, 6]]
Extracted 4. Arr= [[5, 6], [5], [5]]
Extracted 5. Arr= [[6], [5], [5]]
Extracted 5. Arr= [[5], [6]]
Extracted 5. Arr= [[6]]
Extracted 6. Arr= [[]]
Extracted . Arr= [[]]
Note also that this algorithm is also lazy about maintaining the heap property - it is not maintained between calls. This probably causes it to do more work than needed, since it does a complete heapify on each subsequent call. This could be fixed by doing a complete heapify once up front, then calling minheap(a,0) before the return r line.
I ended up writing it myself using the data structures from the 'algorithm' gem. It wasn't as bad as I expected.
require 'algorithms'
class LazyHeapMerger
def initialize(sorted_arrays)
#heap = Containers::Heap.new { |x, y| (x.first <=> y.first) == -1 }
sorted_arrays.each do |a|
q = Containers::Queue.new(a)
#heap.push([q.pop, q])
end
end
def each
while #heap.length > 0
value, q = #heap.pop
#heap.push([q.pop, q]) if q.size > 0
yield value
end
end
end
m = LazyHeapMerger.new([[1, 2], [3, 5], [4]])
m.each do |o|
puts o
end
Here's an implementation which should work on any Enumerable, even infinite ones. It returns Enumerator.
def lazy_merge *list
list.map!(&:enum_for) # get an enumerator for each collection
Enumerator.new do |yielder|
hash = list.each_with_object({}){ |enum, hash|
begin
hash[enum] = enum.next
rescue StopIteration
# skip empty enumerators
end
}
loop do
raise StopIteration if hash.empty?
enum, value = hash.min_by{|k,v| v}
yielder.yield value
begin
hash[enum] = enum.next
rescue StopIteration
hash.delete(enum) # remove enumerator that we already processed
end
end
end
end
Infinity = 1.0/0 # easy way to get infinite range
p lazy_merge([1, 3, 5, 8], (2..4), (6..Infinity), []).take(12)
#=> [1, 2, 3, 3, 4, 5, 6, 7, 8, 8, 9, 10]
No, there's nothing built in to do that. At least, nothing that springs instantly to mind. However, there was a GSoC project to implement the relevant data types a couple of years ago, which you could use.

Resources