Related
I am trying to conceptualize the iteration of two loops
numbers_array = [1,2,3,4,5,6,7,8,9,10]
add_to_array = [1,2,3,4]
While the numbers_array iterates, add_to_array iterates simultaneously adding both elements together at the same time. The caveat is once add_to_array reaches the end, it starts over adding its element to the next index in numbers_array. So at numbers_array[4] we would be adding add_to_array[0] then adding numbers_array[5] to add_to_array[1] and so on. This process would repeat until we reach the end of the numbers_array.
Any input would be greatly appreciated!
You are looking for Enumerable#zip and Enumerable#cycle:
numbers_array = [1,2,3,4,5,6,7,8,9,10]
#⇒ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
add_to_array = [1,2,3,4]
#⇒ [1, 2, 3, 4]
numbers_array.zip(add_to_array.cycle)
#⇒ [[1, 1], [2, 2], [3, 3], [4, 4], [5, 1],
# [6, 2], [7, 3], [8, 4], [9, 1], [10, 2]]
Now do whatever you want with the array returned. E.g. to reduce the zipped result summing elements, map ro Enumerable#sum:
numbers_array.zip(add_to_array.cycle).map(&:sum)
#⇒ [2, 4, 6, 8, 6, 8, 10, 12, 10, 12]
It works by using the % operator to cycle through the indexes.
numbers_array = [1,2,3,4,5,6,7,8,9,10]
add_to_array = [1,2,3,4]
numbers_array.map.with_index do |n, i|
n + add_to_array[i % add_to_array.length]
end
A cool method that's similar, if you didn't want to start over at the next array, would be .zip
https://apidock.com/ruby/Array/zip
add_to_array.zip(*numbers_array.each_slice(add_to_array.size)).
map { |a| a.sum { |e| e.to_i } }
#=> [16, 20, 13, 16]
e.to_i is needed to convert nil values to zeros. See NilClass#to_i.
Another option:
numbers_array.map { |e| e + add_to_array.rotate!.last }
# => [2, 4, 6, 8, 6, 8, 10, 12, 10, 12]
Drawback: add_to_array is mutated by rotate!
I want to write a function that takes in a nested array and return the size of the longest array and itself.
max_with_size([]) # [0, []]
max_with_size([2,3,4]) # [3, [2, 3, 4]]
max_with_size([1,[2,3,4]]) # [3, [2, 3, 4]]
max_with_size([[5,[6],[7,8,9],10,11]]) # [5, [5, [6], [7, 8, 9], 10, 11]]
max_with_size([[1,[2,3,4]],[[[5,[6],[7,8,9],10,11]]]]) # [5, [5, [6], [7, 8, 9], 10, 11]]
So far I've got this
def max_with_size (ary)
max_size = ary.size
max_ary = ary
ary.each { |elem|
if elem.is_a? Array
if elem.size > max_size
max_size = max_with_size(elem)[0]
max_ary = max_with_size(elem)[1]
end
end
}
[max_size, max_ary]
end
It works fine for the first 4 cases, but the 5th fails and only delivers this
max_with_size([[1,[2,3,4]],[[[5,[6],[7,8,9],10,11]]]]) # [2, [[1, [2, 3, 4]], [[[5, [6], [7, 8, 9], 10, 11]]]]]
How can I achieve the wanted result?
The following code should print the desired result. I explained code with Inline comments.
#Initialize #max to empty array, #max is an array with two elements, like this: [max_array_size, max_array]
#max = []
def max_with_size(array)
# when #max is empty or when array size is greater than what is store in #max, store array size and array contents in #max
(#max = [array.size, array]) if #max.empty? || (#max[0] < array.size)
#Iterate through each element in array
array.each do |x|
#Skip to next element if x is not an array
next unless x.is_a? Array
#Recursively find max of array x
max_with_size(x)
end
#max
end
Code
def max_arr(arr)
[arr, *arr.each_with_object([]) {|e,a| a << max_arr(e) if e.is_a?(Array) && e.any?}].
max_by(&:size)
end
Examples
examples = [[],
[2,3,4],
[1,[2,3,4]],
[[5,[6],[7,8,9],10,11]],
[[1,[2,3,4]],[[[5,[6],[7,8,9],10,11]]]],
[1, [2, [3, 4, [6, 7, 8, 9, 10], [11, 12]], 13]]]
examples.each do |arr|
a = max_arr(arr)
puts "\n#{arr}\n \#=> #{a.size}, #{a}"
end·
[]
#=> 0, []
[2, 3, 4]
#=> 3, [2, 3, 4]
[1, [2, 3, 4]]
#=> 3, [2, 3, 4]
[[5, [6], [7, 8, 9], 10, 11]]
#=> 5, [5, [6], [7, 8, 9], 10, 11]
[[1, [2, 3, 4]], [[[5, [6], [7, 8, 9], 10, 11]]]]
#=> 5, [5, [6], [7, 8, 9], 10, 11]
[1, [2, [3, 4, [6, 7, 8, 9, 10], [11, 12]], 13]]
#=> 5, [6, 7, 8, 9, 10]
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(:+) }
I want to take an array of number pairs:
[[2, 3], [2, 15], [3, 15], [7, 8], [8, 7], [11, 15]]
and if two of these number pairs have a number in common, combine them and display the unique values, ultimately creating this array:
[[2, 3, 15, 11], [7, 8]]
Order of the numbers in the output array is not important. What would be the best solution?
The answer was corrected following Howard's comment.
[[2, 3], [2, 15], [3, 2], [3, 15], [7, 8], [8, 7], [11, 15], [15, 2], [15, 3], [15, 11]]
.each_with_object([]) do |e1, a|
a.select{|e2| (e1 & e2).any?}.each{|e2| e1.concat(a.delete(e2))}
e1.uniq!
a.push(e1)
end
# => [[8, 7], [15, 11, 3, 2]]
It's really just another mapping and sorting problem,:
array = [[2, 3], [2, 15], [3, 2], [3, 15], [7, 8], [8, 7], [11, 15], [15, 2], [15, 3], [15, 11]]
array.map{|a| array.each{|x| a |= x if (x & a).any?}; a.sort}.uniq
#=> [[2, 3, 11, 15], [7, 8]]
Not as elegant as #sawa's solution - but works for all the cases:
def includes? item, groups
groups.each{|sub|
return true if sub.include?(item)
}
false
end
def add groups, match, item
groups.each{|sub|
if sub.include?(match)
sub << item
sub.uniq!
return
end
}
end
def iterate arr
groups = []
grp = []
arr.each do |a,b|
grp = [a,b] if grp.empty?
if includes? a, groups
add groups, a, b
elsif includes? b, groups
add groups, b, a
else
groups << [a,b]
end
end
groups
end
arr = [[2, 3], [2, 15], [3, 2], [3, 15], [7, 8], [8, 7], [11, 15], [15, 2], [15, 3], [15, 11]]
p iterate(arr)
OUTPUT
[[2, 3, 15, 11], [7, 8]]
A recursive solution:
def doit(a,b)
return a unless b
h = b.group_by {|e| (a[-1] & e).any?}
h[true] ? a[-1] |= h[true].flatten : a << h[false].pop
doit(a, h[false])
end
arr = [[2, 3], [2, 15], [3, 2], [3, 15], [16, 21], [7, 8], [8, 7], \
[21, 44], [11, 15], [15, 2], [15, 3], [15, 11], [8, 9]]
doit([arr.pop], arr) # => [[8, 9, 7], [15, 11, 2, 3], [21, 44, 16]]
I have a grid that represents an X, Y matrix, stored as a hash here.
Some points on the X Y matrix may have values (as type string), and some may not.
A typical grid could look like this:
{[9, 5]=>"Alaina", [10, 3]=>"Courtney", [11, 1]=>"Gladys", [8, 7]=>"Alford", [14, 11]=>"Lesley", [17, 2]=>"Lawson", [0, 5]=>"Katrine", [2, 1]=>"Tyra", [3, 3]=>"Fredy", [1, 7]=>"Magnus", [6, 9]=>"Nels", [7, 11]=>"Kylie", [11, 0]=>"Kellen", [10, 2]=>"Johan", [14, 10]=>"Justice", [0, 4]=>"Barton", [2, 0]=>"Charley", [3, 2]=>"Magnolia", [1, 6]=>"Maximo", [7, 10]=>"Olga", [19, 5]=>"Isadore", [16, 3]=>"Delfina", [17, 1]=>"Noe", [20, 11]=>"Francis", [10, 5]=>"Creola", [9, 3]=>"Bulah", [8, 1]=>"Lempi", [11, 7]=>"Raquel", [13, 11]=>"Jace", [1, 5]=>"Garth", [3, 1]=>"Ernest", [2, 3]=>"Malcolm", [0, 7]=>"Alejandrin", [7, 9]=>"Marina", [6, 11]=>"Otilia", [16, 2]=>"Hailey", [20, 10]=>"Brandt", [8, 0]=>"Madeline", [9, 2]=>"Leanne", [13, 10]=>"Jenifer", [1, 4]=>"Humberto", [3, 0]=>"Nicholaus", [2, 2]=>"Nadia", [0, 6]=>"Abigail", [6, 10]=>"Zola", [20, 5]=>"Clementina", [23, 3]=>"Alvah", [19, 11]=>"Wallace", [11, 5]=>"Tracey", [8, 3]=>"Hulda", [9, 1]=>"Jedidiah", [10, 7]=>"Annetta", [12, 11]=>"Nicole", [2, 5]=>"Alison", [0, 1]=>"Wilma", [1, 3]=>"Shana", [3, 7]=>"Judd", [4, 9]=>"Lucio", [5, 11]=>"Hardy", [19, 10]=>"Immanuel", [9, 0]=>"Uriel", [8, 2]=>"Milton", [12, 10]=>"Elody", [5, 10]=>"Alexanne", [1, 2]=>"Lauretta", [0, 0]=>"Louvenia", [2, 4]=>"Adelia", [21, 5]=>"Erling", [18, 11]=>"Corene", [22, 3]=>"Haskell", [11, 11]=>"Leta", [10, 9]=>"Terrence", [14, 1]=>"Giuseppe", [15, 3]=>"Silas", [12, 5]=>"Johnnie", [4, 11]=>"Aurelie", [5, 9]=>"Meggie", [2, 7]=>"Phoebe", [0, 3]=>"Sister", [1, 1]=>"Violet", [3, 5]=>"Lilian", [18, 10]=>"Eusebio", [11, 10]=>"Emma", [15, 2]=>"Theodore", [14, 0]=>"Cassidy", [4, 10]=>"Edmund", [2, 6]=>"Claire", [0, 2]=>"Madisen", [1, 0]=>"Kasey", [3, 4]=>"Elijah", [17, 11]=>"Susana", [20, 1]=>"Nicklaus", [21, 3]=>"Kelsie", [10, 11]=>"Garnett", [11, 9]=>"Emanuel", [15, 1]=>"Louvenia", [14, 3]=>"Otho", [13, 5]=>"Vincenza", [3, 11]=>"Tate", [2, 9]=>"Beau", [5, 7]=>"Jason", [6, 1]=>"Jayde", [7, 3]=>"Lamont", [4, 5]=>"Curt", [17, 10]=>"Mack", [21, 2]=>"Lilyan", [10, 10]=>"Ruthe", [14, 2]=>"Georgianna", [4, 4]=>"Nyasia", [6, 0]=>"Sadie", [16, 11]=>"Emil", [21, 1]=>"Melba", [20, 3]=>"Delia", [3, 10]=>"Rosalee", [2, 8]=>"Myrtle", [7, 2]=>"Rigoberto", [14, 5]=>"Jedidiah", [13, 3]=>"Flavie", [12, 1]=>"Evie", [8, 9]=>"Olaf", [9, 11]=>"Stan", [20, 2]=>"Judge", [5, 5]=>"Cassie", [7, 1]=>"Gracie", [6, 3]=>"Armando", [4, 7]=>"Delia", [3, 9]=>"Marley", [16, 10]=>"Robyn", [2, 11]=>"Richie", [12, 0]=>"Gilberto", [13, 2]=>"Dedrick", [9, 10]=>"Liam", [5, 4]=>"Jabari", [7, 0]=>"Enola", [6, 2]=>"Lela", [3, 8]=>"Jade", [2, 10]=>"Johnson", [15, 5]=>"Willow", [12, 3]=>"Fredrick", [13, 1]=>"Beau", [9, 9]=>"Carlie", [8, 11]=>"Daisha", [6, 5]=>"Declan", [4, 1]=>"Carolina", [5, 3]=>"Cruz", [7, 7]=>"Jaime", [0, 9]=>"Anthony", [1, 11]=>"Esta", [13, 0]=>"Shaina", [12, 2]=>"Alec", [8, 10]=>"Lora", [6, 4]=>"Emely", [4, 0]=>"Rodger", [5, 2]=>"Cedrick", [0, 8]=>"Collin", [1, 10]=>"Armani", [16, 5]=>"Brooks", [19, 3]=>"Eleanora", [18, 1]=>"Alva", [7, 5]=>"Melissa", [5, 1]=>"Tabitha", [4, 3]=>"Aniya", [6, 7]=>"Marc", [1, 9]=>"Marjorie", [0, 11]=>"Arvilla", [19, 2]=>"Adela", [7, 4]=>"Zakary", [5, 0]=>"Emely", [4, 2]=>"Alison", [1, 8]=>"Lorenz", [0, 10]=>"Lisandro", [17, 5]=>"Aylin", [18, 3]=>"Giles", [19, 1]=>"Kyleigh", [8, 5]=>"Mary", [11, 3]=>"Claire", [10, 1]=>"Avis", [9, 7]=>"Manuela", [15, 11]=>"Chesley", [18, 2]=>"Kristopher", [24, 3]=>"Zola", [8, 4]=>"Pietro", [10, 0]=>"Delores", [11, 2]=>"Timmy", [15, 10]=>"Khalil", [18, 5]=>"Trudie", [17, 3]=>"Rafael", [16, 1]=>"Anthony"}
What I need to do though, is basically remove all the empty entries.
Let's say [17,3] => Raphael does not have an element in front of if (let's say - no [16,3] exists) then [17,3] should become [16,3] etc.
So basically all empty items will be popped off the vertical (row) structure of the hash.
Are there functions I should have a look at or is there an easy squash-like method that would just remove blanks and adjust and move other items?
Thanks in advance for your help.
Here's my shot at it (probably not the fastest possible):
data.group_by{|k,v| k[1]}.inject({}){|a,(k,v)|
v.sort_by{|i| i[0][0]}.each_with_index{|elem,i|
a[[i,elem[0][1]]] = elem[1]
}
a
}
And here's input subset suitable for testing:
{[9, 5]=>"Alaina", [10, 3]=>"Courtney", [11, 5]=>"Gladys", [8, 5]=>"Alford"}
which should result with:
{[0, 5]=>"Alford", [1, 5]=>"Alaina", [2, 5]=>"Gladys", [0, 3]=>"Courtney"}
Step-by-step explanation (you can experiment in irb):
We need group-by because we should process each row separately, so first we group records by row, making a hash of arrays (let's call each of these arrays r)
data.group_by{|k,v| k[1]}
# => {5=>[[[9, 5], "Alaina"], [[11, 5], "Gladys"], [[8, 5], "Alford"]], 3=>[[[10, 3], "Courtney"]]}
inject is there so we can create output hash, we start with the empty one and we'll add "transformed" element at a time. Transformation is simply replacing the column indexes with "compacted" indexes, so the idea is to somehow map array [8, 9, 11] to [0, 1, 2] (when processing row 5). That's why we need to sort array r (let's call the sorted array rs):
r = [[[9, 5], "Alaina"], [[11, 5], "Gladys"], [[8, 5], "Alford"]]
rs = r.sort_by{|i| i[0][0]}
#=> [[[8, 5], "Alford"], [[9, 5], "Alaina"], [[11, 5], "Gladys"]]
And then assign each element in rs an index, starting from 0. We use each_with_index for that, so that in elem, i we get pairs such as
[[8, 5], "Alford"], 0
[[9, 5], "Alaina"], 1
[[11, 5], "Gladys"], 2
and now we have all needed numbers to fill in the resulting hash a:
a[[i,elem[0][1]]] = elem[1]
so
a[[0,5]] = 'Alford'
a[[1,5]] = 'Alaina'
etc.
We need to return a (accumulator) from a block for inject to work properly.
That should be it. :)
Ok... I think i get what you want it to do; Here is how I would do it:
data = # the hash in the format above
rows = [] # a new array
# then place each row in the entry of rows corresponding to it's y value at
# the index of it's x value
data.each_pair {|key, value| rows[key[1]] ||= []; rows[key[1]][key[0]] = value }
# compact the x values (if there is an adjacent lower x available move to it)
# this is what i believe you meant in your example
rows.map(&:compact!)
# clear out the old data
data = {}
# fill the hash back up using the new compacted data
rows.each_with_index { |xs, y| xs.each_with_index { |entry, x| data[[x,y]] = entry } }
tested it on your data (along with adding some more difficult x values) and it seems to work :)
Roja