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]
# ]
Related
I have a Ruby nested array like this:
arr = [["Red", 2], ["Red", 1], ["Yellow", 0], ["Yellow", 2], ["Blue", 1]]
And I'm trying to find a method that adds up the number (which is the second item in each mini-array) for similar items, yielding a nested array like this:
[["Red", 3], ["Yellow", 2], ["Blue", 1]]
I've tried for loops, and if statements, but I can't figure out exactly how to do it.
With a non-nested array you could just use if and .includes?, but I'm not sure how to work that with a nested array.
Any Ruby wizards able to steer me in the right direction here?
Note that this is not an array nor valid ruby
["Red", 3], ["Yellow", 2], ["Blue", 1]
anyway try this
arr.group_by { |color, amount| color }
.map { |k, color_arr| [k, color_arr.map(&:last).inject(:+)] }
=> [["Red", 3], ["Yellow", 2], ["Blue", 1]]
or from ruby 2.4.0 and upwards
arr.group_by { |color, amount| color }
.map { |k, color_arr| [k, color_arr.map(&:last).sum] }
arr.each_with_object(Hash.new(0)) {|(col,nbr),h| h[col] += nbr}.to_a
#=> [["Red", 3], ["Yellow", 2], ["Blue", 1]]
Note the intermediate calculation:
arr.each_with_object(Hash.new(0)) {|(col,nbr),h| h[col] += nbr}
#=> {"Red"=>3, "Yellow"=>2, "Blue"=>1}
See Hash::new.
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] } } }
}
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"]
# ]
I am attempting to write a method named my_transform that takes an array as follows:
items = ["Aqua", "Blue", "Green", "Red", "Yellow"]
and displays the items' indexes as follows:
item_to_position = {"Aqua"=>0, "Blue"=>1, "Green"=>2, "Red"=>3, "Yellow"=>4}
I should be able to execute:
my_transform(items) == item_to_position
and receive true.
I have contemplated using each_with_index. Should I begin by saying:
items = ["Aqua", "Blue", "Green", "Red", "Yellow"]
hash = Hash[*array]
def my_transform
I have to convert the string to a hash. Any help is appreciated.
I would use Array#to_h:
items = ["Aqua", "Blue", "Green", "Red", "Yellow"]
items.each_with_index.to_h
#=> { "Aqua"=>0, "Blue"=>1, "Green"=>2, "Red"=>3, "Yellow"=>4 }
Note that to_h was introduced in Ruby 2.1
Using to_h your my_transform method could look like this:
def my_transform(items)
items.each_with_index.to_h
end
You can do this in various ways.
Create an array and convert it to a hash
Until fairly recently, you would use the public class method Hash::[] to convert an array to a hash. It works like this:
h = Hash[ [[:a, 1], [:b, 2]] ]
#=> {:a=>1, :b=>2}
or
h = Hash[:a, 1, :b, 2]
#=> {:a=>1, :b=>2}
In Ruby v2.1.0 the methods Array#to_h and Enumerable#to_h were introduced. The first works like this:
h = [[:a, 1], [:b, 2]].to_h
#=> {:a=>1, :b=>2}
Therefore, to use Hash or to_h you must first create the array:
arr1 = [["Aqua", 0], ["Blue", 1], ["Green", 2], ["Red", 3], ["Yellow", 4]]
or
arr2 = ["Aqua", 0, "Blue", 1, "Green", 2, "Red", 3, "Yellow", 4]
In the second case we'd use it like this:
Hash[*arr2]
#=> {"Aqua"=>0, "Blue"=>1, "Green"=>2, "Red"=>3, "Yellow"=>4}
Let's first create arr1. You are right that you need to use Enumerable#each_with_index. You then need to use Enumerable#to_a to convert each element of items to an array [<color>, index].
items = ["Aqua", "Blue", "Green", "Red", "Yellow"]
arr = items.each_with_index.to_a
#=> [["Aqua", 0], ["Blue", 1], ["Green", 2], ["Red", 3], ["Yellow", 4]]
Let's look at this more closely:
enum = items.each_with_index
#=> #<Enumerator: ["Aqua", "Blue", "Green", "Red", "Yellow"]:each_with_index>
enum, an enumerator, is an instance of the class Enumerator. The Enumerator class is one of many classes that includes the Enumerable module, of which to_a is an instance method. Not only does:
arr = enum.to_a
#=> [["Aqua", 0], ["Blue", 1], ["Green", 2], ["Red", 3], ["Yellow", 4]]
convert the enumerator to the desired array, but it is a convenient way to view the elements of any enumerator (which are generally passed to either a block or to another enumerator).
So we can now create the hash:
h = Hash[arr]
#=> {"Aqua"=>0, "Blue"=>1, "Green"=>2, "Red"=>3, "Yellow"=>4}
or
h = Hash[*arr.flatten]
#=> {"Aqua"=>0, "Blue"=>1, "Green"=>2, "Red"=>3, "Yellow"=>4}
or
h = arr.to_h
#=> {"Aqua"=>0, "Blue"=>1, "Green"=>2, "Red"=>3, "Yellow"=>4}
Suppose now that we had:
items = ["Aqua", "Blue", "Green", "Aqua", "Aqua"]
We then obtain:
items.each_with_index.to_a.to_h
#=> {"Aqua"=>4, "Blue"=>1, "Green"=>2}
In building the hash, Ruby first creates the key-value pair "Aqua"=>0, which she later overwrites with "Aqua"=>3 and then with "Aqua"=>4. This is a consequence of the fact that hashes have unique keys.
Build the hash from scratch
Now suppose we start with an empty hash:
h = {}
(same as h = Hash.new) and add key-value pairs:
items = ["Aqua", "Blue", "Green", "Red", "Yellow"]
items.each_index { |i| h[items[i]] = i }
#=> ["Aqua", "Blue", "Green", "Red", "Yellow"]
h #=> {"Aqua"=>0, "Blue"=>1, "Green"=>2, "Red"=>3, "Yellow"=>4}
We could alternatively write:
items.size.times { |i| h[items[i]] = i }
#=> 5
h #=> {"Aqua"=>0, "Blue"=>1, "Green"=>2, "Red"=>3, "Yellow"=>4}
or
(0...items.size).each { |i| h[items[i]] = i }
#=> 0...5
h #=> {"Aqua"=>0, "Blue"=>1, "Green"=>2, "Red"=>3, "Yellow"=>4}
The Ruby way is skip the step h = {} and to use each_with_index, as before, together with Enumerator#with_object:
items.each_with_index.with_object({}) { |(s,i),h| h[s] = i }
#=> {"Aqua"=>0, "Blue"=>1, "Green"=>2, "Red"=>3, "Yellow"=>4}
The "object" in with_object is a hash, with_object's argument being its initial value, here an empty hash. This object is represented by the block variable h and is returned after all elements of items have been enumerated (so we don't need a subsequent line h to return the hash).
Lets look at the steps that are performed here. First, we have
enum0 = items.each_with_index
#=> #<Enumerator: ["Aqua", "Blue", "Green", "Red", "Yellow"]:each_with_index>
which I discussed earlier. Then Ruby computes
enum1 = enum0.with_object({})
#=> #<Enumerator: #<Enumerator: ["Aqua", "Blue", "Green", "Red", "Yellow"]
:each_with_index>:with_object({})>
Examine the return value carefully. As you see, enum1, like enum0, is an enumerator. You might think of it as a "compound enumerator". To see the values of enum1 that will be passed to the block, you can convert it to an array:
enum1.to_a
#=> [[["Aqua", 0], {}], [["Blue", 1], {}], [["Green", 2], {}],
# [["Red", 3], {}], [["Yellow", 4], {}]]
As you see, enum1 has five elements, each an array containing an array and a hash. The elements of enum1 are passed to the block by Enumerator#each, (which calls Array#each):
enum1.each { |(s,i),h| h[s] = i }
#=> {"Aqua"=>0, "Blue"=>1, "Green"=>2, "Red"=>3, "Yellow"=>4}
We can use Enumerator#next to pass each element of enum1 to the block, and set the block variables to its value. The first is:
(s,i),h = enum1.next
#=> [["Aqua", 0], {}]
s #=> "Aqua"
i #=> 0
h #=> {}
Notice how [["Aqua", 0], {}] is decomposed into its three constituent elements and each block variable is set equal to one of the elements. This is called array decomposition.
We can now perform the block calculation:
h[s] = i
#=> {}["Aqua"] = 0
so now:
h #=> {"Aqua"=>0}
Then the second element is passed to the block:
(s,i),h = enum1.next
#=> [["Blue", 1], {"Aqua"=>0}]
s #=> "Blue"
i #=> 1
h #=> {"Aqua"=>0}
Notice how h has been updated. The block calculation is now:
h[s] = i
#=> {"Aqua"=>0}["Blue"] = 1
and now:
h #=> {"Aqua"=>0, "Blue"=>1}
The remaining calculations are performed similarly. After all elements of enum1 have been enumerated, enum1.each returns h.
def my_transform(arr)
arr.inject({}) {|m,e| m[e] = arr.index(e); m }
end
items = ["Aqua", "Blue", "Green", "Red", "Yellow"]
def my_transform(items)
Hash[items.each_with_index.map { |value, index| [value, index] }]
end
Most Rubies
This works at least as far back as Ruby 1.9.3.
# Verbose, but flexible!
def hasherize *array
hash = {}
array.flatten!
array.each_with_index { |key, value| hash[key] = value }
hash
end
# Pass a single array as an argument.
hasherize %w(Aqua Blue Green Red Yellow)
#=> {"Aqua"=>0, "Blue"=>1, "Green"=>2, "Red"=>3, "Yellow"=>4}
# Pass multiple arguments to the method.
hasherize :foo, :bar, :baz
#=> {:foo=>0, :bar=>1, :baz=>2}
Ruby >= 2.1.0
If you're running a recent Ruby, you can simplify the above to:
def hasherize *array
array.flatten.each_with_index.to_h
end
The results will be the same as above, but the Array#to_h method simplifies the code a lot. However, you still need to flatten the array to avoid results like:
#=> {["Aqua", "Blue", "Green", "Red", "Yellow"]=>0}
You can also try this.
e.g.
items = ["Aqua", "Blue", "Green", "Red", "Yellow"]
items.inject({}) do |tmphash, (k,v)|
tmphash[k] = items.index(k)
tmphash
end
## OUTPUT
{"Aqua"=>0, "Blue"=>1, "Green"=>2, "Red"=>3, "Yellow"=>4}
I use nvd3-line-chart angularjs directive.
My data is:
$scope.exampleData = [
{
"key": "Series 1",
"values": [ [1, 2], [2, 10], [3, 6], [4, 15], [5, 8] ]
},
{
"key": "Series 2",
"values": [ [1, 5], [2, 30], [3, 8], [4, 11], [5, 4] ]
},
{
"key": "Series 3",
"values": [ [1, 2000], [2, 10000], [3, 600], [4, 1500], [5, 8000] ]
}
]
Html:
nvd3-line-chart(
data="exampleData"
id="graph-Data-1"
width="1170"
height="494"
showXAxis="true"
showYAxis="true"
tooltips="true"
useInteractiveGuideline="true"
showLegend="true"
ng-if="exampleData"
)
How i can assign Y2 to Series 3 data? I found line-plus-bar-chart directive but how can i implement this via nvd3-line-chart directive?
I'm afraid you can't use your data to render a secondary axis. It is out of the scope of the data itself.
The best thing you could do is to trick it yourself: http://www.d3noob.org/2013/01/using-multiple-axes-for-d3js-graph.html