Groovy for Rubyists - #each_slice - ruby

In ruby I have #each_slice...
(1..10).each_slice(3).to_a
=> [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]]
I'm looking for an elegant way to do the same in Groovy and this is all I got so far as a beginner:
arr = []
list = (1..10) as Queue
while(!list.isEmpty()) {
sub_arr = []
3.times { sub_arr << list.poll() }
sub_arr.removeAll([null])
arr << sub_arr
}
arr
Result: [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]]

You need collate in groovy
(1..10).collate(3)

Related

Find and return the longest array in a nested array with its size

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]

get an array of arrays with unique elements

I have an array like this:
[1, 2, 3, 3, 4, 4, 5, 6, 6, 6, 7]
I want to know if there's a method to get this:
[[1, 2, 3, 4, 5, 6, 7], [3, 4, 6], [6]]
I know there is Array.uniq but this removes the duplicate elements, and I would like to keep them.
Not sure about performance, but this works:
Code:
$ cat foo.rb
require 'pp'
array = [1, 2, 3, 3, 4, 4, 5, 6, 6, 6, 7]
result = []
values = array.group_by{|e| e}.values
while !values.empty?
result << values.map{|e| e.slice!(0,1)}.flatten
values = values.reject!{|e| e.empty?}
end
pp result
Output:
$ ruby foo.rb
[[1, 2, 3, 4, 5, 6, 7], [3, 4, 6], [6]]
A simple solution, but I'm sure it will not have the best performance:
def array_groups(arr)
result = []
arr.uniq.each do |elem|
arr.count(elem).times do |n|
result[n] ||= []
result[n] << elem
end
end
result
end
array_groups [1, 2, 3, 3, 4, 4, 5, 6, 6, 6, 7]
# [[1, 2, 3, 4, 5, 6, 7], [3, 4, 6], [6]]
[1, 2, 3, 3, 4, 4, 5, 6, 6, 6, 7]
.each.with_object([]){|e, a| (a.find{|b| !b.include?(e)} || a.push([]).last).push(e)}
# => [[1, 2, 3, 4, 5, 6, 7], [3, 4, 6], [6]]
On ruby you could add a method to the class Array. Like this:
class Array
def uniqA (acc)
return acc if self.empty?
# return self.replace acc if self.empty? #to change the object itself
acc << self.uniq
self.uniq.each { |x| self.delete_at(self.index(x)) }
uniqA(acc)
end
end
b = [1, 2, 3, 3, 4, 4, 5, 6, 6, 6, 7]
print b.uniqA([])
#=> [[1, 2, 3, 4, 5, 6, 7], [3, 4, 6], [6]]
print b
#=> []
Or you could do this, to keep the elements on b:
b = b.uniqA([])
#=> [[1, 2, 3, 4, 5, 6, 7], [3, 4, 6], [6]]
print b
#=> [[1, 2, 3, 4, 5, 6, 7], [3, 4, 6], [6]]
Here are a couple of ways of doing it.
arr = [1, 2, 3, 3, 4, 4, 5, 6, 6, 6, 7]
#1
b = []
a = arr.dup
while a.any?
u = a.uniq
b << u
a = a.difference u
end
b
#=> [[1, 2, 3, 4, 5, 6, 7], [3, 4, 6], [6]]
The helper Array#difference is defined in my answer here.
#2
arr.map { |n| [n, arr.count(n)] }
.each_with_object({}) { |(n,cnt),h|
(1..cnt).each { |i| (h[i] ||= []) << n } }
.values
.map(&:uniq)
#=> [[1, 2, 3, 4, 5, 6, 7], [3, 4, 6], [6]]
The steps, for:
arr = [1, 2, 3, 3, 6, 6, 6, 7]
a = arr.map { |n| [n, arr.count(n)] }
#=> [[1, 1], [2, 1], [3, 2], [3, 2], [4, 2], [4, 2],
# [5, 1], [6, 3], [6, 3], [6, 3], [7, 1]]
enum = a.each_with_object({})
#=> #<Enumerator: [[1, 1], [2, 1], [3, 2], [3, 2], [4, 2], [4, 2],
# [5, 1], [6, 3], [6, 3], [6, 3], [7, 1]]:each_with_object({})>
To view the elements of enum:
enum.to_a
#=> [[[1, 1], {}], [[2, 1], {}],...[[7, 1], {}]]
Now step through the enumerator and examine the hash after each step:
(n,cnt),h = enum.next
#=> [[1, 1], {}]
n #=> 1
cnt #=> 1
h #=> {}
(1..cnt).each { |i| (h[i] ||= []) << n }
h #=> {1=>[1]}
(n,cnt),h = enum.next
#=> [[2, 1], {1=>[1]}]
(1..cnt).each { |i| (h[i] ||= []) << n }
h #=> {1=>[1, 2]}
(n,cnt),h = enum.next
#=> [[3, 2], {1=>[1, 2]}]
(1..cnt).each { |i| (h[i] ||= []) << n }
h #=> {1=>[1, 2, 3], 2=>[3]}
(n,cnt),h = enum.next
#=> [[3, 2], {1=>[1, 2, 3], 2=>[3]}]
(1..cnt).each { |i| (h[i] ||= []) << n }
h #=> {1=>[1, 2, 3, 3], 2=>[3, 3]}
(n,cnt),h = enum.next
#=> [[6, 3], {1=>[1, 2, 3, 3], 2=>[3, 3]}]
(1..cnt).each { |i| (h[i] ||= []) << n }
h #=> {1=>[1, 2, 3, 3, 6], 2=>[3, 3, 6], 3=>[6]}
(n,cnt),h = enum.next
#=> [[6, 3], {1=>[1, 2, 3, 3, 6], 2=>[3, 3, 6], 3=>[6]}]
(1..cnt).each { |i| (h[i] ||= []) << n }
h #=> {1=>[1, 2, 3, 3, 6, 6], 2=>[3, 3, 6, 6], 3=>[6, 6]}
(n,cnt),h = enum.next
#=> [[6, 3], {1=>[1, 2, 3, 3, 6, 6], 2=>[3, 3, 6, 6], 3=>[6, 6]}]
(1..cnt).each { |i| (h[i] ||= []) << n }
h #=> {1=>[1, 2, 3, 3, 6, 6, 6], 2=>[3, 3, 6, 6, 6], 3=>[6, 6, 6]}
(n,cnt),h = enum.next
#=> [[7, 1], {1=>[1, 2, 3, 3, 6, 6, 6], 2=>[3, 3, 6, 6, 6], 3=>[6, 6, 6]}]
(1..cnt).each { |i| (h[i] ||= []) << n }
h #=> {1=>[1, 2, 3, 3, 6, 6, 6, 7], 2=>[3, 3, 6, 6, 6], 3=>[6, 6, 6]}
Lastly, extract and uniqify the values of the hash:
b = h.values
#=> [[1, 2, 3, 3, 6, 6, 6, 7], [3, 3, 6, 6, 6], [6, 6, 6]]
b.map(&:uniq)
#=> [[1, 2, 3, 6, 7], [3, 6], [6]]

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(:+) }

divide an array into multiple arrays, each array in which the number of units is determined by the size

For example,i have an array :[2,4,6,7,9,12,1],i want to divide it by the size [2,2,3]
the output that i want is:[[2,4],[6,7],[9,12,1]]
i have tried:
a=[2,4,6,7,9,12,1]
b=[2,2,3]
c=[]
b.each{|m|c<<a.shift(m)}
c
is there an easier way to do it?
You can use Enumerable#map:
a = [2,4,6,7,9,12,1]
b = [2,2,3]
c = b.map { |m| a.shift(m) }
c
# => [[2, 4], [6, 7], [9, 12, 1]]
The way looks correct, you can do it more succinctly using map instead of each:
c = b.map{|m| a.shift(m)}
Or, using &method shorthand:
c = b.map(&a.method(:shift))
# => [[2, 4], [6, 7], [9, 12, 1]]
Here are two ways that do not destroy the original array, as Array#shift does:
a=[2,4,6,7,9,12,1]
b=[2,2,3]
Method #1
cum = 0
b.map { |n| a[cum..(cum+=n)-1] }
#=> [[2, 4], [6, 7], [9, 12, 1]]
Method #2
cum = 0
b.map { |n| a.values_at(*(cum..(cum+=n)-1)) }
#=> [[2, 4], [6, 7], [9, 12, 1]]
You can have:
r = [2, 4, 6, 7, 9, 12, 1]
s = [2, 2, 3].map do |e|
r.shift(e)
end
p s
Output:
[[2, 4], [6, 7], [9, 12, 1]]
this will work
2.1.0 :029 > a=[2,4,6,7,9,12,1]
=> [2, 4, 6, 7, 9, 12, 1]
2.1.0 :030 > b = [2,2,3]
=> [2, 2, 3]
2.1.0 :031 > b.inject([]){|result, value| result << a.take(value) }
=> [[2, 4], [2, 4], [2, 4, 6]]
2.1.0 :032 >

How to perform function to subarray inside ruby multidimensional array

I have a multidimensional array in ruby like this one:
a = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
How do I add "1" to each element. For instance, I want to end up with something like this:
a = [[2, 3, 4], [5, 6, 7], [8, 9, 10]]
Thanks in advance!
There might be a slightly more clever one liner but this is fairly clear.
a.map { |ar| ar.map { |e| e + 1 } }
Just for fun :
class Array
def increment
map(&:next)
end
end
#Tada!
a.map(&:increment)
a.map { |xs| xs.map(&:succ) }
#=> [[2, 3, 4], [5, 6, 7], [8, 9, 10]]

Resources