I want to transform an existing array to display it. Therefore I cut the array in half but add the content of the cut element to the remained elements.
# source structure
s = [[1, 'blue'],
[2, 'red'],
[3, 'yellow'],
[4, 'green'],
[5, 'orange'],
[6, 'black']]
# result structure
format_array(s)
# [[1, 'blue', 4, 'green'],
# [2, 'red', 5, 'orange'],
# [3, 'yellow', 6, 'black']]
How would you achieve it?
a = [[1, "blue"], [2, "red"], [3, "yellow"], [4, "green"], [5, "orange"], [6, "black"]]
first, last = a.first(a.size / 2), a.last(a.size / 2)
#=> [[[1, "blue"], [2, "red"], [3, "yellow"]], [[4, "green"], [5, "orange"], [6, "black"]]]
first.zip(last).map(&:flatten)
# [
# [1, "blue", 4, "green"],
# [2, "red", 5, "orange"],
# [3, "yellow", 6, "black"]
# ]
Just one more solution:
a.each_slice(a.size / 2).to_a.transpose.map(&:flatten)
#=> [[1, "blue", 4, "green"], [2, "red", 5, "orange"], [3, "yellow", 6, "black"]]
s.each_slice((s.size + 1) / 2).reduce(&:zip).map(&:flatten)
Step1: Divide array into two using each_slice method of array. each_slice documentation
Step2: Use array.zip method to map self with corresponding elements of array. zip documentation
Step3: Use flatten to flatten the array. Flatten documentation
s => [[1, "blue"], [2, "red"], [3, "yellow"], [4, "green"], [5, "orange"], [6, "black"]]
s1,s2 = s.each_slice((s.length)/2).to_a
==> [[[1, "blue"], [2, "red"], [3, "yellow"]], [[4, "green"], [5, "orange"], [6, "black"]]]
s1.zip(s2).map(&:flatten)
=> [[1, "blue", 4, "green"], [2, "red", 5, "orange"], [3, "yellow", 6, "black"]]
s = [[1, 'blue'], [2, 'red'], [3, 'yellow'], [4, 'green'], [5, 'orange'], [6, 'black']]
# Split into two sections
s1 = s[0...s.length/2]
s2 = s[s.length/2..-1]
# Compile
p s1.each_with_index.map { |x, i| x + s2[i] }
#[[1, "blue", 4, "green"], [2, "red", 5, "orange"], [3, "yellow", 6, "black"]]
a maths trick -))
s.group_by {|a| a[0]%((s.length)/2) }.values.map {|e| e.flatten }
# [
# [1, "blue", 4, "green"],
# [2, "red", 5, "orange"],
# [3, "yellow", 6, "black"]
# ]
Related
func combinations<T>(of array: [[T]]) -> [[T]] {
return array.reduce([[]]) { combihelper(a1: $0, a2: $1) }
}
func combihelper<T>(a1: [[T]], a2: [T]) -> [[T]] {
var x = [[T]]()
for elem1 in a1 {
for elem2 in a2 {
x.append(elem1 + [elem2])
}
}
return x
}
What's the best solution to write the code in one func and more swifty?
If all you want is to combine both methods into a single one just change a1 to $0 and a2 to $1:
func combinations<T>(of array: [[T]]) -> [[T]] {
return array.reduce([[]]) {
var x = [[T]]()
for elem1 in $0 {
for elem2 in $1 {
x.append(elem1 + [elem2])
}
}
return x
}
}
let multi = [[1,2,3,4,5],[1,2,3,4,5,6,7,8,9,0]]
combinations(of: multi) // [[1, 1], [1, 2], [1, 3], [1, 4], [1, 5], [1, 6], [1, 7], [1, 8], [1, 9], [1, 0], [2, 1], [2, 2], [2, 3], [2, 4], [2, 5], [2, 6], [2, 7], [2, 8], [2, 9], [2, 0], [3, 1], [3, 2], [3, 3], [3, 4], [3, 5], [3, 6], [3, 7], [3, 8], [3, 9], [3, 0], [4, 1], [4, 2], [4, 3], [4, 4], [4, 5], [4, 6], [4, 7], [4, 8], [4, 9], [4, 0], [5, 1], [5, 2], [5, 3], [5, 4], [5, 5], [5, 6], [5, 7], [5, 8], [5, 9], [5, 0]]
Extending Collection, constraining the element to RangeReplaceableCollection and using higher-order functions we can came up with:
extension Collection where Element: RangeReplaceableCollection {
func combinations() -> [Element] {
reduce([.init()]) { result, element in
result.flatMap { elements in
element.map { elements + CollectionOfOne($0) }
}
}
}
}
let strings = ["12345","1234567890"]
strings.combinations() // ["11", "12", "13", "14", "15", "16", "17", "18", "19", "10", "21", "22", "23", "24", "25", "26", "27", "28", "29", "20", "31", "32", "33", "34", "35", "36", "37", "38", "39", "30", "41", "42", "43", "44", "45", "46", "47", "48", "49", "40", "51", "52", "53", "54", "55", "56", "57", "58", "59", "50"]
You could also do it without any for loops:
func combinations<T>(of array: [[T]]) -> [[T]]
{
return array.reduce([[]]){ c,a in c.flatMap{ e in a.map{e + [$0] } } }
}
In Ruby, how do I go from this:
[
1,
["green", "blue", "black"],
[ ["1", "2"], ["3"], ["4", "5"] ]
]
to this?
[
[1, "green", "1"],
[1, "green", "2"],
[1, "blue", "3"],
[1, "black", "4"],
[1, "black", "5"],
]
I tried .zip but with no luck. Any help is greatly appreciated, and of course I'm looking for a performant solution.
The pattern is not completely clear to me, but this gets the expected output for the example you provided:
data[1].
zip(data[2]).
flat_map { |x, ys| [x].product(ys) }.
map { |zs| [data[0], *zs] }
#=> [[1, "green", "1"], [1, "green", "2"], [1, "blue", "3"],
# [1, "black", "4"], [1, "black", "5"]]
We are given
arr = [1, ["green", "blue", "black"], [ ["1", "2"], ["3"], ["4", "5"] ]]
Here are a couple of ways to obtain the desired result.
#1
arr[1].flat_map.with_index { |color,i| [arr[0]].product([color], arr[2][i]) }
#=> [[1, "green", "1"], [1, "green", "2"], [1, "blue", "3"],
# [1, "black", "4"], [1, "black", "5"]]
The steps are as follows.
enum0 = arr[1].flat_map
#=> #<Enumerator: ["green", "blue", "black"]:flat_map>
enum1 = enum0.with_index
#=> #<Enumerator: #<Enumerator: ["green", "blue", "black"]:flat_map>:with_index>
enum1 can be thought of as a compound enumerator. We can see the values that will be generated by enum1 and passed to the block by converting it to an array.
enum1.to_a
#=> [["green", 0], ["blue", 1], ["black", 2]]
The first value is generated and passed to the block, the block variables are assigned and the block calculation is performed.
color, i = enum1.next
#=> ["green", 0]
color
#=> "green"
i #=> 0
[arr[0]].product([color], arr[2][i])
#=> [1].product(["green"], )
#=> [[1, "green", "1"], [1, "green", "2"]]
The calculations are similar for the two remaining elements generated by enum1.
An alternative to this is to dup arr[2] and shift elements of the dup:
a2 = arr[2].dup
arr[1].flat_map { |color,i| [arr[0]].product([color], a2.shift) }
#2
arr[1].zip(arr[2]).flat_map { |color, a| [arr[0]].product([color], a) }
#=> [[1, "green", "1"], [1, "green", "2"], [1, "blue", "3"],
# [1, "black", "4"], [1, "black", "5"]]
The steps are as follows.
b = arr[1].zip(arr[2])
#=> [["green", ["1", "2"]], ["blue", ["3"]], ["black", ["4", "5"]]]
b[0] is passed to flat_map and the block variables are assigned and the block calculation is performed.
color, a = b[0]
#=> ["green", ["1", "2"]]
color
#=> "green"
a #=> ["1", "2"]
[arr[0]].product([color], a)
#=> [["1"]].product(["green"], ["1", "2"])
#=> [[1, "green", "1"], [1, "green", "2"]]
After the remaining elements of b are passed to map the desired array is returned by Enumerable#flat_map.
I needed a more generic and flexible solution as compared to the ones proposed (my bad, I should have been more clear about the requirements), so I came up with the following:
class Array
def transpose_rows
arys = self.select{|el| el.is_a?(Array)}
if arys.size == 0
[self]
else
result = []
(arys.map(&:size).max || 1).times.map{ |i|
self.map { |r|
r.is_a?(Array) ? r[i] : r
}.transpose_rows.map{|r| result << r}
}
result
end
end
end
The initial spec is that every element in the array is either a value or another array of varying depth. Each subarray "explodes" the values of the subarrays of depth-1 into an arbitrary number of "sub-values". The result should be a set of rows listing all combinations deriving from the original array.
The other solutions proposed do work for the original array I posted, which was just an example, while this one works for more complex scenarios such as the following:
[
2,
[3,4,5],
6,
[7,8,9],
[ [11,22], [33], [44,55] ],
[0, 1, 2],
[ [66], [77], [nil,99] ],
4
].transpose_rows
# => [
# [2, 3, 6, 7, 11, 0, 66, 4],
# [2, 3, 6, 7, 22, 0, nil, 4],
# [2, 4, 6, 8, 33, 1, 77, 4],
# [2, 5, 6, 9, 44, 2, nil, 4],
# [2, 5, 6, 9, 55, 2, 99, 4]
# ]
I have an array of arrays. I want to concatenate the first, second, third elements of arrays.
Example arrays:
a = [[4, 5, 6], [1, 2, 3], [8, 9, 10]]
a1 = [[1, 2, 3], [8, 9, 10]]
a2 = [[4, 5, 6], [1, 2, 3], [8, 9, 10], [11, 21, 31]]
Output:
out of a: [[4,1,8],[5,2,9],[6,3,10]]
out of a1: [[1,8],[2,9],[3,10]]
out of a2: [[4,1,8,11],[5,2,9,21],[6,3,10,31]]
Use transpose method
a.transpose
=> [[4, 1, 8], [5, 2, 9], [6, 3, 10]]
Array#transpose:
[a, a1, a2].map(&:transpose)
# [
# [[4, 1, 8], [5, 2, 9], [6, 3, 10]],
# [[1, 8], [2, 9], [3, 10]],
# [[4, 1, 8, 11], [5, 2, 9, 21], [6, 3, 10, 31]]
# ]
Whenever Array#transpose can be used so can Enumerable#zip.
a.first.zip *a.drop(1)
#=> [[4,1,8],[5,2,9],[6,3,10]]
In Ruby, if I have an array with duplicates in it and a given value n:
array = [1,56,1,245,56,1,56,1,56,1,56]
n = 2
How could I limit the number of duplicates within the array to n whilst also retaining the positioning of the remaining elements to produce this?
array = [1,56,1,245,56]
def ndup(list, n)
cnts = {}
list.select do |item|
cnts[item] ||= 0
cnts[item] += 1
cnts[item] <= n
end
end
ndup([1,56,1,245,56,1,56,1,56,1,56], 2)
#=> [1, 56, 1, 245, 56]
def ff(array, n)
occurances = Hash.new(0)
array.uniq do |element|
occurances[element] = occurances[element] < n ? occurances[element].next : n
[element, occurances[element]]
end
end
Using a counting hash is the way to go here, but here's an alternative:
array.each_with_index.
group_by(&:first).
values.
flat_map { |a| a.first(n) }.
sort_by(&:last).
map(&:first)
#=> [1, 56, 1, 245, 56]
The steps are as follows.
enum = array.each_with_index
#=> #<Enumerator: [1, 56, 1, 245, 56, 1, 56, 1, 56, 1, 56]:each_with_index>
h = enum.group_by(&:first)
#=> { 1=>[[1, 0], [1, 2], [1, 5], [1, 7], [1, 9]],
# 56=>[[56, 1], [56, 4], [56, 6], [56, 8], [56, 10]],
# 245=>[[245, 3]]}
a = h.values
#=> [[[1, 0], [1, 2], [1, 5], [1, 7], [1, 9]],
# [[56, 1], [56, 4], [56, 6], [56, 8], [56, 10]],
# [[245, 3]]]
b = a.flat_map { |a| a.first(n) }
#=> [[1, 0], [1, 2], [56, 1], [56, 4], [245, 3]]
c = b.sort_by(&:last)
#=> [[1, 0], [56, 1], [1, 2], [245, 3], [56, 4]]
c.map(&:first)
#=> [1, 56, 1, 245, 56]
I want to do the following:
array1 = [[1, 10], [2, 20], [3, 10], [4, 30]]
array2 = [[1, 10], [2, 10], [3, 5], [4, 10]]
I want to add two arrays in such a way that the second element of each subarray will be added. I want the following output.
result = [[1,20],[2,30],[3,15],[4,40]]
[array1, array2].transpose.map{|(k, v1), (_, v2)| [k, v1 + v2]}
# => [[1, 20], [2, 30], [3, 15], [4, 40]]
Another approach as below :
array1 = [[1,10],[2,20],[3,10],[4,30]]
array2 = [[1,10],[2,10],[3,5],[4,10]]
Hash[array1].merge(Hash[array2]) { |key,old,new| old + new }.to_a
# => [[1, 20], [2, 30], [3, 15], [4, 40]]
Taking the help of merge(other_hash){|key, oldval, newval| block} .
This can be achieved with a combination of Array#zip and Array#map:
result = array1.zip(array2).map { |l, r| [l[0], l[1] + r[1]] }
#=> [[1, 20], [2, 30], [3, 15], [4, 40]]
However, key-value pairs are often best treated as a Hash. Among other operations, this will allow you to #merge:
hash1
#=> {1=>10, 2=>20, 3=>10, 4=>30}
hash2
#=> {1=>10, 2=>10, 3=>5, 4=>10}
result = hash1.merge(hash2) { |_, l, r| l + r }
#=> {1=>20, 2=>30, 3=>15, 4=>40}