Ruby - Sort multidimensional array by first line - arrays

I need to sort a multidimensional array by first line.
The first line (array[0][0] to array[0][n-1]) is composed of strings, I need to sort it, and the other lines to follow up...
I already searched a lot and saw how to use the sort function to sort it by column, but didn't found out how to apply it to my problem...
I already solved my problem with a bubble sort who sort the first line, then report the change to the other lines if there's one, but I wondered if there was a better way to do it ?
Creation of the array :
array = Array.new(4) { Array.new(var, 0) }
I have something like that in it :
[ [ "A 1", "A 3", "A 2", "A 4" ],
[ 4, 5, 6, 7 ],
[ 2, 2, 2, 2 ],
[ 0.1, 0.2, 0.1, 0.2 ] ]
The expected result would be as followed :
[ [ "A 1", "A 2", "A 3", "A 4" ],
[ 4, 6, 5, 7 ],
[ 2, 2, 2, 2 ],
[ 0.1, 0.1, 0.2, 0.2 ] ]

You can use Array#transpose and Enumerable#sort_by to handle this like so:
arr = [ [ "A 1", "A 3", "A 2", "A 4" ],
[ 4, 5, 6, 7 ],
[ 2, 2, 2, 2 ],
[ 0.1, 0.2, 0.1, 0.2 ] ]
Array#transpose turns rows into columns:
arr.transpose
#=> [["A 1", 4, 2, 0.1],
# ["A 3", 5, 2, 0.2],
# ["A 2", 6, 2, 0.1],
# ["A 4", 7, 2, 0.2]]
Then we just need to sort by the first column values sort_by(&:first):
arr.transpose.sort_by(&:first)
#=> [["A 1", 4, 2, 0.1],
# ["A 2", 6, 2, 0.1],
# ["A 3", 5, 2, 0.2],
# ["A 4", 7, 2, 0.2]]
Then we just transpose back again:
arr.transpose.sort_by(&:first).transpose
#=> [["A 1", "A 2", "A 3", "A 4"],
# [4, 6, 5, 7],
# [2, 2, 2, 2],
# [0.1, 0.1, 0.2, 0.2]]
The same could be achieved by zipping the Arrays together like so: (but the former seems like a better choice)
arr.reduce(&:zip).sort_by {|a| a.flatten!.first}.transpose
#=> [["A 1", "A 2", "A 3", "A 4"],
# [4, 6, 5, 7],
# [2, 2, 2, 2],
# [0.1, 0.1, 0.2, 0.2]]

Less Ruby'ish approach:
sorted = arr.first.sort
# => ["A 1", "A 2", "A 3", "A 4"]
order_arr = arr.first.map { |x| sorted.index(x) }
# => [0, 2, 1, 3]
arr.map { |a| order_arr.map { |x| a[x] } }
# => [["A 1", "A 2", "A 3", "A 4"],
# [4, 6, 5, 7],
# [2, 2, 2, 2],
# [0.1, 0.1, 0.2, 0.2]]

Related

Combine two Dictionaries

I'm learning Swift and can't find the solution for my problem...
I have two Dictionaries and want to combine them:
dict1 = ["A": 1, "B": 2, "D": 5]
dict2 = ["A": 3, "C": 9, "D": 4]
The outcome should be a new Dictionary like:
dict3 = ["A": 4, "B": 2, "C": 9, "D": 9]
You can use Dictionary merging method and pass the plus sign (addition operator) as the uniquingKeysWith parameter:
let dict3 = dict1.merging(dict2, uniquingKeysWith: +) // ["A": 4, "B": 2, "D": 9, "C": 9]

Every Value Combination of Dictionary

Been racking my brains and can't seem to come up with an elegant solution. Wondering if anyone can help me.
I have a Swift dictionary with an array of values for each key like so...
[A:[1, 2, 3], B:[4, 5, 6], C:[7, 8, 9]]
And I need to convert it into an array of dictionaries with every combination of values like so...
[
[A:1, B:4, C:7],
[A:1, B:4, C:8],
[A:1, B:4, C:9],
[A:1, B:5, C:7],
[A:1, B:5, C:8],
[A:1, B:5, C:9],
[A:1, B:6, C:7],
[A:1, B:6, C:8],
[A:1, B:6, C:9],
[A:2, B:4, C:7],
[A:2, B:4, C:8],
[A:2, B:4, C:9],
[A:2, B:5, C:7],
[A:2, B:5, C:8],
[A:2, B:5, C:9],
[A:2, B:6, C:7],
...
[A:3, B:6, C:9],
]
Here's a recursive solution that works for any number of key/array pairs in the original dictionary, and for any number of values in the values arrays:
let dict = ["A":[1, 2, 3], "B":[4, 5, 6], "C":[7, 8, 9]]
func combine(source: [String : [Int]], partials: [[String : Int]] = [], final: inout [[String : Int]]) {
if source.isEmpty {
// base step of recursion
// there are no more (k:v) pairs in source, so add all of the partials (now final)
// to the final array
final.append(contentsOf: partials)
} else {
// source has a (k:v) pair, so take the first one
let (k, vals) = source.first!
var newsource = source
// remove key from newsource
newsource[k] = nil
// for each value in the key
for val in vals {
// add (k:v) to each partial dictionary
var newpartials = partials
// If new partials array is empty
if newpartials.isEmpty {
// create the array with the first [k:v]
newpartials = [[k : val]]
} else {
// otherwise, add [k:v] to each of the partial dictionaries
for pidx in newpartials.indices {
newpartials[pidx][k] = val
}
}
// recursive call to process the next value in source
combine(source: newsource, partials: newpartials, final: &final)
}
}
}
var result = [[String : Int]]()
combine(source: dict, final: &result)
print(result)
print(result.count)
[["A": 1, "B": 4, "C": 7], ["A": 2, "B": 4, "C": 7], ["A": 3, "B": 4, "C": 7], ["C": 7, "B": 5, "A": 1], ["C": 7, "B": 5, "A": 2], ["C": 7, "B": 5, "A": 3], ["C": 7, "B": 6, "A": 1], ["C": 7, "B": 6, "A": 2], ["C": 7, "B": 6, "A": 3], ["C": 8, "B": 4, "A": 1], ["C": 8, "B": 4, "A": 2], ["C": 8, "B": 4, "A": 3], ["C": 8, "B": 5, "A": 1], ["C": 8, "B": 5, "A": 2], ["C": 8, "B": 5, "A": 3], ["B": 6, "C": 8, "A": 1], ["B": 6, "C": 8, "A": 2], ["B": 6, "C": 8, "A": 3], ["A": 1, "C": 9, "B": 4], ["A": 2, "C": 9, "B": 4], ["A": 3, "C": 9, "B": 4], ["B": 5, "A": 1, "C": 9], ["B": 5, "A": 2, "C": 9], ["B": 5, "A": 3, "C": 9], ["B": 6, "A": 1, "C": 9], ["B": 6, "A": 2, "C": 9], ["B": 6, "A": 3, "C": 9]]
27
A generic solution
There's no reason to limit this to just String and Int. The keys should be any type that conforms to Hashable.
To make the function generic, change the signature to:
func combine<KEY, VALUE>(source: [KEY : [VALUE]], partials: [[KEY : VALUE]] = [],
final: inout [[KEY : VALUE]]) where KEY: Hashable {
Making a Dictionary extension
#LeoDabus extended this answer by turning it into an extension on Dictionary where the Value is a Collection (Thanks, Leo!):
extension Dictionary where Value: Collection {
func permutations() -> [[Key: Value.Element]] {
guard !isEmpty else { return [] }
var permutations: [[Key: Value.Element]] = []
permutate(&permutations)
return permutations
}
private func permutate(_ permutations: inout [[Key: Value.Element]], _ dictionaries: [[Key: Value.Element]] = []) {
if let (key, value) = first {
var dictionary = self
dictionary[key] = nil
for element in value {
var dictionaries = dictionaries
if dictionaries.isEmpty {
dictionaries += CollectionOfOne([key: element])
} else {
for index in dictionaries.indices {
dictionaries[index][key] = element
}
}
dictionary.permutate(&permutations, dictionaries)
}
} else {
permutations += dictionaries
}
}
}
let dict = ["A":[1, 2, 3], "B":[4, 5, 6], "C":[7, 8, 9]]
let result = dict.permutations()
print(result)
print(result.count)
[["B": 4, "A": 1, "C": 7], ["B": 4, "A": 2, "C": 7], ["B": 4, "A": 3, "C": 7], ["A": 1, "B": 4, "C": 8], ["A": 2, "B": 4, "C": 8], ["A": 3, "B": 4, "C": 8], ["A": 1, "B": 4, "C": 9], ["A": 2, "B": 4, "C": 9], ["A": 3, "B": 4, "C": 9], ["C": 7, "A": 1, "B": 5], ["C": 7, "A": 2, "B": 5], ["C": 7, "A": 3, "B": 5], ["C": 8, "A": 1, "B": 5], ["C": 8, "A": 2, "B": 5], ["C": 8, "A": 3, "B": 5], ["C": 9, "A": 1, "B": 5], ["C": 9, "A": 2, "B": 5], ["C": 9, "A": 3, "B": 5], ["B": 6, "C": 7, "A": 1], ["B": 6, "C": 7, "A": 2], ["B": 6, "C": 7, "A": 3], ["B": 6, "A": 1, "C": 8], ["B": 6, "A": 2, "C": 8], ["B": 6, "A": 3, "C": 8], ["B": 6, "A": 1, "C": 9], ["B": 6, "A": 2, "C": 9], ["B": 6, "A": 3, "C": 9]]
27
Are you sure you need an array of dictionaries in the end? If not, here's a jagged array of key-value pairs:
let keyValuePairArrays =
["A": [1, 2, 3], "B": [4, 5, 6], "C": [7, 8, 9]]
.sorted(\.key)
.map { key, value in
value.map { (key, $0) }
}
.combinations
If you really need the dictionaries, just map it! 🗺
keyValuePairArrays.map(Dictionary.init)
Supporting code:
public extension Sequence where Element: Sequence {
var combinations: [[Element.Element]] {
guard let initialResult = ( first?.map { [$0] } )
else { return [] }
return dropFirst().reduce(initialResult) { combinations, iteration in
combinations.flatMap { combination in
iteration.map { combination + [$0] }
}
}
}
}
public extension Sequence {
/// The first element of the sequence.
/// - Note: `nil` if the sequence is empty.
var first: Element? {
var iterator = makeIterator()
return iterator.next()
}
/// Sorted by a common `Comparable` value.
func sorted<Comparable: Swift.Comparable>(
_ comparable: (Element) throws -> Comparable
) rethrows -> [Element] {
try sorted(comparable, <)
}
/// Sorted by a common `Comparable` value, and sorting closure.
func sorted<Comparable: Swift.Comparable>(
_ comparable: (Element) throws -> Comparable,
_ areInIncreasingOrder: (Comparable, Comparable) throws -> Bool
) rethrows -> [Element] {
try sorted {
try areInIncreasingOrder(comparable($0), comparable($1))
}
}
}

Changing the Key Names in a [Dictionary<String, String>] Array

I have an array ([Dictionary<String, String>]) of dictionary, say,
let dict0 = ["0": 1, "1": 2, "2": 4]
let dict1 = ["0": 5, "1": 4, "2": 8]
let dict2 = ["0": 3, "1": 9, "2": 7]
let array = [dict0, dict1, dict2]
So it looks like the following.
[
["0": 1, "1": 2, "2": 4],
["2": 8, "0": 5, "1": 4],
["2": 7, "1": 9, "0": 3]
]
Let me assume that I have an array ([String]) of keys like
let keys = ["ant", "bee", "spider"]
Is there a simple way of changing my array's keys such that it will look like the following?
[
["ant": 1, "bee": 2, "spider": 4],
["spider": 8, "ant": 5, "bee": 4],
["spider": 7, "bee": 9, "ant": 3]
]
Thanks.
Don't actually know what you mean by simple way but the following will get the job done.
let dict0 = ["0": 1, "1": 2, "2": 4]
let dict1 = ["0": 5, "1": 4, "2": 8]
let dict2 = ["0": 3, "1": 9, "2": 7]
let array = [dict0, dict1, dict2]
let keys = ["ant", "bee", "spider"]
let keysIndexedDictionary = Dictionary(uniqueKeysWithValues: keys.enumerated().map { (String($0), $1) })
let mappedArray = array.map { dic -> [String : Int] in
// I wish this can be shortened using $ notation
let mappedTuples = dic.flatMap { [(keysIndexedDictionary[$0] ?? $0) : $1] }
return Dictionary(uniqueKeysWithValues: mappedTuples)
}

Ruby: multidimensional array to one-dimensional rows

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]
# ]

angularjs nvd3-line-chart set y2 axis

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

Resources