Related
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))
}
}
}
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)
}
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]]
I use this code :
AppDelegate.monthList = [Int: String]()
for index in stride(from: 1, to: 12, by: 1) {
AppDelegate.monthList[index] = "\(index)"
}
print("iosLog MONTH: \(AppDelegate.monthList)")
And result is :
iosLog MONTH: [11: "11", 10: "10", 2: "2", 4: "4", 9: "9", 5: "5", 6:
"6", 7: "7", 3: "3", 1: "1", 8: "8"]
Whay ?!
I want add respectively the keys ( like PHP or Java )
Because Dictionary is unordered collection:
Every dictionary is an unordered collection of key-value pairs.
So, if you are aiming to get a sorted version of it, you should -logically- transform it into ordered collection, which is array. You could get:
Sorted array of AppDelegate.monthList keys:
let sortedkeys = AppDelegate.monthList.keys.sorted()
// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
Sorted array of AppDelegate.monthList values:
let sortedValues = AppDelegate.monthList.values.sorted()
// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
Or sorted array of tuples, as [(key, value)]:
let sortedTuples = AppDelegate.monthList.sorted(by: <)
for tuple in sortedTuples {
print("\(tuple.key): \(tuple.value)")
}
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]
# ]