Suppose I have the following array:
a = (1..10).to_a
Is there a single in-built ruby (or rails) function that is capable or splitting the array into exactly N roughly equal parts while maintaining the order?
I'm looking for something like this:
a.bucketize(3)
=> [[1,2,3,4],[5,6,7],[8,9,10]]
a.bucketize(5)
=> [[1,2],[3,4],[5,6],[7,8],[9,10]]
Hint: each_slice doesn't do this.
Also, I know I could write this function myself and open up the Array class or Enumerable module.
Thanks.
I'd do it like this:
ary = (1..10).to_a
ary.each_slice((ary.length.to_f/3).ceil).to_a
=> [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10]]
ary.each_slice((ary.length.to_f/5).ceil).to_a
=> [[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]]
It's not perfect, but it does come close:
ary = (1..9).to_a
=> [1, 2, 3, 4, 5, 6, 7, 8, 9]
ary.each_slice((ary.length.to_f/2).ceil).to_a
=> [[1, 2, 3, 4, 5], [6, 7, 8, 9]]
ary.each_slice((ary.length.to_f/3).ceil).to_a
=> [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
ary.each_slice((ary.length.to_f/4).ceil).to_a
=> [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
This kind of task is best tackled using a functional approach. Here's a tail-recursive functional implementation (except for the unavoidable << to accumulate efficiently on arrays):
class Array
def bucketize(n, index = 0, acc = [])
return acc if n <= 0 || size <= index
n0 = ((size - index).to_f / n).ceil
bucketize(n - 1, index + n0, acc << self[index, n0])
end
end
(1..9).to_a.bucketize(3)
#=> [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
(1..10).to_a.bucketize(3)
#=> [[1, 2, 3, 4], [5, 6, 7], [8, 9, 10]]
(1..11).to_a.bucketize(3)
#=> [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11]]
Here's what I ended up doing:
class Array
def bucketize(n)
return [] if (buckets = n.to_i) <= 0
j = length / buckets.to_f
result = each_with_index.chunk { |_, i| (i / j).floor }.map { |_, v| v.map(&:first) }
result << [] until result.length == buckets
result
end
end
Examples:
a = (1..10).to_a
=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
a.bucketize(1)
=> [[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]]
a.bucketize(2)
=> [[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]]
a.bucketize(3)
=> [[1, 2, 3, 4], [5, 6, 7], [8, 9, 10]]
a.bucketize(4)
=> [[1, 2, 3], [4, 5], [6, 7, 8], [9, 10]]
...
a.bucketize(9)
=> [[1, 2], [3], [4], [5], [6], [7], [8], [9], [10]]
...
a.bucketize(11)
=> [[1], [2], [3], [4], [5], [6], [7], [8], [9], [10], []]
Related
def attack(k,x):
for i in range(0,k-1):
if x[i]==x[k] or abs(x[i]-x[k])==abs(i-k):
return 1
return 0
def nQueens(n):
x=[]
for i in range(0,n):
x.append(-1)
k=0
solution=[]
while k>=0:
x[k]=x[k]+1
while x[k]<=n-1 and attack(k,x)==1:
x[k]=x[k]+1
if x[k]<=n-1:
if k==n-1:
solution.append(x)
else:
k=k+1
x[k]=0
else:
k=k-1
return solution
n=4
print(nQueens(n))
I tried to solve n queens problem, but my program returns
[[4, 4, 4, 4], [4, 4, 4, 4], [4, 4, 4, 4], [4, 4, 4, 4], [4, 4, 4, 4], [4, 4, 4, 4], [4, 4, 4, 4], [4, 4, 4, 4], [4, 4, 4, 4], [4, 4, 4, 4], [4, 4, 4, 4], [4, 4, 4, 4], [4, 4, 4, 4], [4, 4, 4, 4], [4, 4, 4, 4], [4, 4, 4, 4]]
instead of [[2, 4, 1, 3], [3, 1, 4, 2]]. Any suggestions where am I wrong?
[ 1, 1, 3, 5 ] & [ 1, 2, 3 ] #=> [ 1, 3 ]
[ 'a', 'b', 'b', 'z' ] & [ 'a', 'b', 'c' ] #=> [ 'a', 'b' ]
I need the intersection of each array with all other arrays within an array.
So the array could look like ->
a = [[1, 2, 3], [3, 4, 5], [4, 5, 6]]
The result should look like ->
a = [[3],[3,4,5][4,5]]
Any suggestions?
Look into the combination method.
a = [[1, 2, 3], [3, 4, 5], [4, 5, 6],[1,"a","b"]]
p a.combination(2).map{|x,y| x & y } #=> [[3], [], [1], [4, 5], [], []]
And if you do not want the empty arrays in there:
p a.combination(2).map{|x,y| x & y }.reject(&:empty?) #=> [[3], [1], [4, 5]]
Edit: After seeing some examples what OP actually want here is how I would achieve the desired result:
original = [[1, 2, 3], [3, 4, 5], [4, 5, 6]]
def intersect_with_rest(array)
array.size.times.map do
first, *rest = array
array.rotate!
first & rest.flatten
end
end
p intersect_with_rest(original) #=> [[3], [3, 4, 5], [4, 5]]
p original #=> [[1, 2, 3], [3, 4, 5], [4, 5, 6]]
Or:
original = [[1, 2, 3], [3, 4, 5], [4, 5, 6]]
result = original.map.with_index do |x,i|
x & (original[0...i]+original[1+i..-1]).flatten
end
p result #=> [[3], [3, 4, 5], [4, 5]]
Yeah, finally I found a solution. Maybe there is a simpler way, but that works for me now..
c = [[1,2,3],[3,4,5],[4,5,6]]
results = [];c.length.times.each {|e| results.push c.rotate(e).combination(2).map {|x, y| x & y}}
results.map{|x, y| y + x}
=> [[3], [3, 4, 5], [4, 5]]
Thanks to #hirolau for the hint. Best regards
Is there any inbuilt method available to split large array into smaller chunks with dynamic fall factor?
Eg: i=0
src_arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
if batch_size = 5 and fall_factor = 1, first chunk should be [1, 2, 3, 4, 5] and subsequent array chunks should start from start_index = i * (batch_size - fall_factor). ie, start_index will be 0, 4, 8, 12, and
result: [1, 2, 3, 4, 5]
[5, 6, 7, 8, 9]
[9, 10, 11, 12, 13]
[13, 14]
if fall_factor = 2 result should be as below
[1, 2, 3, 4, 5]
[4, 5, 6, 7, 8]
[7, 8, 9, 10, 11]
[10, 11, 12, 13, 14]
I know how to SOLVE this. My question is if any inbuilt method available like each_slice to get this done instead of reinventing.
For example you can use just #step method of Numeric
0.step(src_arr.size - fall_factor - 1, batch_size - fall_factor).map do |ind|
src_arr[ind, batch_size]
end
# fall_factor = 1
# => [[1, 2, 3, 4, 5], [5, 6, 7, 8, 9], [9, 10, 11, 12, 13], [13, 14]]
# fall_factor = 2
# => [[1, 2, 3, 4, 5], [4, 5, 6, 7, 8], [7, 8, 9, 10, 11], [10, 11, 12, 13, 14]]
Code
def doit(arr, batch_size, fall_factor)
arr[batch_size..-1].
each_slice(batch_size-fall_factor).
each_with_object([arr[0,batch_size]]) { |b,c| c << [*c.last[-fall_factor..-1], *b] }
end
Examples
arr = (1..14).to_a
#=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
doit(arr, 5, 1)
#=> [[1, 2, 3, 4, 5], [5, 6, 7, 8, 9], [9, 10, 11, 12, 13], [13, 14]]
doit(arr, 5, 2)
#=> [[1, 2, 3, 4, 5], [4, 5, 6, 7, 8], [7, 8, 9, 10, 11], [10, 11, 12, 13, 14]]
doit(arr, 5, 3)
#=> [[1, 2, 3, 4, 5], [3, 4, 5, 6, 7], [5, 6, 7, 8, 9], [7, 8, 9, 10, 11],
# [9, 10, 11, 12, 13], [11, 12, 13, 14]]
doit(arr, 5, 4)
#=> [[1, 2, 3, 4, 5], [2, 3, 4, 5, 6], [3, 4, 5, 6, 7], [4, 5, 6, 7, 8],
# [5, 6, 7, 8, 9], [6, 7, 8, 9, 10], [7, 8, 9, 10, 11], [8, 9, 10, 11, 12],
# [9, 10, 11, 12, 13], [10, 11, 12, 13, 14]]
Explanation
For arr above and:
batch_size = 5
fall_factor = 2
we have:
a = arr[batch_size..-1]
#=> arr[5..-1]
#=> [6, 7, 8, 9, 10, 11, 12, 13, 14]
b = a.each_slice(batch_size-fall_factor)
#=> a.each_slice(3)
#=> #<Enumerator: [6, 7, 8, 9, 10, 11, 12, 13, 14]:each_slice(3)>
We can see the elements of the enumerator b by converting it to an array:
b.to_a
#=> [[6, 7, 8], [9, 10, 11], [12, 13, 14]]
Continuing:
d = [arr[0,batch_size]]
#=> [[1, 2, 3, 4, 5]]
b.each_with_object(d) { |b,c| c << [*c.last[-fall_factor..-1], *b] }
#=> [[1, 2, 3, 4, 5], [4, 5, 6, 7, 8], [7, 8, 9, 10, 11], [10, 11, 12, 13, 14]]
To see how the last calculation is performed, let:
e = b.each_with_object(d)
#=> #<Enumerator: #<Enumerator: [6, 7, 8, 9, 10, 11, 12, 13, 14]:
# each_slice(3)>:each_with_object([[1, 2, 3, 4, 5]])>
e.to_a
#=> [[[6, 7, 8], [[1, 2, 3, 4, 5]]],
# [[9, 10, 11], [[1, 2, 3, 4, 5]]],
# [[12, 13, 14], [[1, 2, 3, 4, 5]]]]
We can use Enumerator#next to obtain each element of e that is passed to the block, set the block variables to each of those values and perform the block calculation. The first element is passed to the block:
b, c = e.next
#=> [[6, 7, 8], [[1, 2, 3, 4, 5]]]
b #=> [6, 7, 8]
c #=> [[1, 2, 3, 4, 5]]
The block calculation is therefore:
c << [*c.last[-fall_factor..-1], *b]
#=> c << [*[[1, 2, 3, 4, 5]].last[-2..-1], *[6, 7, 8]]
# c << [*[1, 2, 3, 4, 5][-2..-1], *[6, 7, 8]]
# c << [*[4, 5], *[6, 7, 8]]
# c << [4, 5, 6, 7, 8]
c #=> [[1, 2, 3, 4, 5], [4, 5, 6, 7, 8]]
The next element of e is now passed to the block:
b, c = e.next
#=> [[9, 10, 11], [[1, 2, 3, 4, 5], [4, 5, 6, 7, 8]]]
b #=> [9, 10, 11]
c #=> [[1, 2, 3, 4, 5], [4, 5, 6, 7, 8]]
The remaining calculations are performed similarly.
Based on logic shared by you, below is one possible implementation:
b = 5 # batch size
f = 2 # fall factor
indices = (0...src_arr.size).collect {|i| i * (b-f)}.reject {|i| i + f >= src_arr.size}
result = indices.each_with_object([]) do |i, obj|
obj << src_arr[i, b]
end
p result
#=> [[1, 2, 3, 4, 5], [4, 5, 6, 7, 8], [7, 8, 9, 10, 11], [10, 11, 12, 13, 14]]
I'm having trouble explaining what I am looking for so I will provide an example, let's say I have this array:
[
[1, 2],
[1, 3],
[1, 4],
[2, 3],
[2, 4],
[3, 4]
]
rather than sorting it by the first column, I would like it to cycle through the first column, so instead of 1, 1, 1, 2, 2, 3 it would do: 1, 2, 3, 1, 2, 1
resulting in:
[
[1, 2],
[2, 3],
[3, 4],
[1, 3],
[2, 4],
[1, 4]
]
Even better would be if it could cycle through both columns to prevent two numbers in a row as much as possible, the ideal solution would sort the original array as:
[
[1, 2],
[3, 4],
[1, 3],
[2, 4],
[1, 4],
[2, 3]
]
Leading to the maximum spacing between repeating numbers for each inner array (both columns being taken into account).
I hope I have provided sufficient information, and I will greatly appreciate any advise, I am fairly clueless so far, searching has yeilded me nothing.
I will only address the first part of your question as I don't understand what you mean by "Even better would be if it could cycle through both columns to prevent two numbers in a row as much as possible...". The clause "as much as possible" is especially troublesome, as it refers to an unspecified criterion.
Let arr be your array. The elements are sorted in your example, but if they were not, the first step would be:
arr.sort!
See Array#sort! and Array#<=> for an explanation of how Ruby sorts arrays whose elements are arrays.
There are many ways to obtain the desired ordering. Here is one that uses Enumerable#chunk:
arr.chunk(&:first).flat_map {|_,a| a.map.with_index {|i,b| [b,i]}}.sort.map(&:last)
#=> [[1, 2], [2, 3], [3, 4], [1, 3], [2, 4], [1, 4]]
The steps are as follows:
e = arr.chunk(&:first)
#=> #<Enumerator: #<Enumerator::Generator:0x007fa01a8141d0>:each>
We can see the elements of this enumerator, which are passed to the block by Enumerator#each (which calls Array#each), by converting it to an array:
e.to_a
#=> [[1, [[1, 2], [1, 3], [1, 4]]], [2, [[2, 3], [2, 4]]], [3, [[3, 4]]]]
Continuing:
f = e.flat_map { |_,a| a.map.with_index { |i,b| [b,i] } }
#=> [[0, [1, 2]], [1, [1, 3]], [2, [1, 4]], [0, [2, 3]], [1, [2, 4]], [0, [3, 4]]]
g = f.sort
#=> [[0, 1, 2], [0, 2, 3], [0, 3, 4], [1, 1, 3], [1, 2, 4], [2, 1, 4]]
g.map(&:last)
#=> [[1, 2], [2, 3], [3, 4], [1, 3], [2, 4], [1, 4]]
Let's look more closely at the calculation of f:
h = e.flat_map
#=> #<Enumerator: #<Enumerator: #<Enumerator::Generator:0x007fa01a8141d0>:each>:flat_map>
h.to_a
#=> [[1, [[1, 2], [1, 3], [1, 4]]], [2, [[2, 3], [2, 4]]], [3, [[3, 4]]]]
You can think of h as a "compound" enumerator.
The first value of h, [1, [[1, 2], [1, 3], [1, 4]]], is passed to the block and captured by the block variables using parallel (or multiple) assignment:
i, a = h.next
#=> [1, [[1, 2], [1, 3], [1, 4]]]
i #=> 1
a #=> [[1, 2], [1, 3], [1, 4]]
As i is not used in the block calculation, it is customary to replace that block variable with the local variable _.
We can now perform the block calculation:
a.map.with_index { |i,b| [b,i] }
#=> [[0, [1, 2]], [1, [1, 3]], [2, [1, 4]]]
The remaining calculations are performed similarly.
you could try this
def func ary
ret = []
# group by first ones, and each sort by second ones
a = ary.group_by{|i| i[0]}.map{|_,i| i.sort_by{|j| j[1]}}
# add to ret
(0...a.map{|i| i.size}.max).map{
a.map{|i| ret << i.shift}
}
ret.compact
end
a = [[1, 2],[1, 3],[1, 4],[2, 3],[2, 4],[3, 4]]
p func(a)
#=> [[1, 2], [2, 3], [3, 4], [1, 3], [2, 4], [1, 4]]
Assuming the initial array is sorted by the first element:
arr =
[
[1, 2],
[1, 3],
[1, 4],
[2, 3],
[2, 4],
[3, 4],
]
res = []
arr_dup = arr.dup
remaining_values = arr_dup.map { |el| el[0] }
current_value = remaining_values.first
loop do
arr_dup.each_with_index do |el, index|
if el[0] >= current_value
res << el
current_value = remaining_values.select { |v| v > el[0] }.first || remaining_values.first
remaining_values.delete_at(remaining_values.index(current_value))
arr_dup.delete_at(index)
break
end
end
break if remaining_values.empty?
end
p arr #=> [[1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]]
p res #=> [[1, 2], [2, 3], [3, 4], [1, 3], [2, 4], [1, 4]]
Few tests:
[[1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4], [5, 1], [20, 2]] =>
[[1, 2], [2, 3], [3, 4], [5, 1], [20, 2], [1, 3], [2, 4], [1, 4]]
[[1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4], [5, 1], [5, 2], [20, 2]] =>
[[1, 2], [2, 3], [3, 4], [5, 1], [20, 2], [1, 3], [2, 4], [5, 2], [1, 4]]
I'm trying to transform an array of 3 arrays in an array of 4 where each array is built and ordered with the first element of each other.
I have this:
[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]
And I would like the following:
[[1, 4, 7, 10], [2, 5, 8, 11], [3, 6, 9, 12]]
Any ideas?
Use Array#transpose.
your_array.transpose
will do it.
array = [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]
array.transpose
[[1, 4, 7, 10], [2, 5, 8, 11], [3, 6, 9, 12]]
Is this what you were looking for?