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

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)
}

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]

Spilt Array into pairs

I am trying to split array into pairs. I am able to split into consecutive pair but I want to split into pair which includes previous value as mentioned in Results
Logic to split in consecutive pair, which I tried.
extension Array {
func chunks(_ chunkSize: Int) -> [[Element]] {
return stride(from: 0, to: self.count, by: chunkSize).map {
Array(self[$0..<Swift.min($0 + chunkSize, self.count)])
}
}
Array:
["1", "2", "3", "4", "5", "6", "7", "8", "9"]
Results:
[["1", "2"], ["2", "3"], ["3", "4"], ["4", "5"], ["6", "7"], ["7", "8"], ["8", "9"]]
How about this:
let a = [ 1, 2, 3, 4, 5, 6, 7, 8, 9]
let pairs = Array(zip(a, a.dropFirst())).map {[$0.0, $0.1] }
print(pairs)
That outputs
[[1, 2], [2, 3], [3, 4], [4, 5], [5, 6], [6, 7], [7, 8], [8, 9]]
Edit:
If you want arbitrary chunk-size, you could write the extension like this:
extension Array {
func chunks(_ chunkSize: Int, includePartialChunk: Bool = true) -> [[Element]] {
var indexes = Array<Int>(stride(from: 0, to: count, by: chunkSize - 1))
if includePartialChunk,
let last = indexes.last,
last < count - 1 {
indexes.append(count-1)
}
return zip(indexes, indexes.dropFirst()).map {Array(self[$0.0...$0.1])}
}
}
Use the parameter includePartialChunk to tell the function if you want to include a "partial chunk" at the end when the array size is not an even multiple of the chunk-size. If true (The default) it returns a last chunk that is smaller than chunkSize but goes to the end of the array. If false, it only returns full-sized chunks, but will skip array elements at the end that don't fit into a full-sized chunk.
(I'll have to study Leo's UnfoldSequence version and see if I can adapt my code to that.)
Not a direct answer to the question but the elements of the sequence should be computed lazily. You should use Swift UnfoldSequence type as follow:
extension Collection {
var unfoldedNeighbors: UnfoldSequence<SubSequence,Index> {
sequence(state: startIndex) { start in
guard start < endIndex else { return nil }
guard let end = index(start, offsetBy: 2, limitedBy: endIndex) else {
return nil
}
defer { formIndex(after: &start) }
return self[start..<end]
}
}
var neighborsSubsequences: [SubSequence] {
.init(unfoldedNeighbors)
}
var neighborsArrays: [[Element]] {
unfoldedNeighbors.map([Element].init)
}
}
Usage:
let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9]
for neighbors in numbers.unfoldedNeighbors {
print(neighbors)
}
If you need control the number of elements of each subsequence and also if it includes the tail or not:
extension Collection {
func unfoldedNeighbors(limitedTo length: Int, includesTail: Bool = false) -> UnfoldSequence<SubSequence,Index> {
sequence(state: startIndex) { start in
guard start < endIndex else { return nil }
guard let end = index(start, offsetBy: length, limitedBy: endIndex) else {
if includesTail {
defer { formIndex(&start, offsetBy: length-1, limitedBy: endIndex) }
return self[start...]
}
return nil
}
defer { formIndex(&start, offsetBy: length-1, limitedBy: endIndex) }
return self[start..<end]
}
}
func neighborsSequences(limitedTo length: Int, includesTail: Bool = false) -> [SubSequence] {
.init(unfoldedNeighbors(limitedTo: length, includesTail: includesTail))
}
func neighborsArrays(limitedTo length: Int, includesTail: Bool = false) -> [[Element]] {
unfoldedNeighbors(limitedTo: length, includesTail: includesTail).map([Element].init)
}
}
Usage:
let numbers = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
for neighbors in numbers.unfoldedNeighbors(limitedTo: 3, includesTail: true) {
print(neighbors)
}
This will print:
[1, 2, 3]
[3, 4, 5]
[5, 6, 7]
[7, 8, 9]
[9, 10]
let neighborsSequences = a.neighborsSequences(limitedTo: 3, includesTail: true) // [[1, 2, 3], [3, 4, 5], [5, 6, 7], [7, 8, 9], [9, 10]] of type [Array<Int>.SubSequence]
let neighborsArrays = a.neighborsArrays(limitedTo: 3, includesTail: true) // [[1, 2, 3], [3, 4, 5], [5, 6, 7], [7, 8, 9], [9, 10]] of type [[Int]]
This operation goes by windows. It does exactly what you've requested, for count 2…
import Algorithms
Array(["1", "2", "3", "4", "5", "6", "7", "8", "9"].windows(ofCount: 2))
…but it's unclear if it does what you want for other counts.

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))
}
}
}

Swift: append into dictionary return random

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)")
}

Swift2 - Sort multiple arrays based on the sorted order of another INT array

let points:[Int] = [200, 1000, 100, 500]
let people:[String] = ["Harry", "Jerry", "Hannah", "John"]
let peopleIds:[Int] = [1, 2, 3, 4]
let sex:[String] = ["Male", "Male", "Female", "Male"]
How can I sort this arrays by points to be?:
let points:[Int] = [1000, 500, 200, 100]
let people:[String] = ["Jerry", "John", "Harry", "Hannah"]
let peopleIds:[Int] = [2, 4, 1, 3]
let sex:[String] = ["Male", "Male", "Male", "Female"]
It's not duplicate of How to sort 1 array in Swift / Xcode and reorder multiple other arrays by the same keys changes
I've tried with the answers and it's not working
Create a new array of indexes sorted the way you want "descending" and then map the other arrays.
var points:[Int] = [200, 1000, 100, 500]
var people:[String] = ["Harry", "Jerry", "Hannah", "John"]
var peopleIds:[Int] = [1, 2, 3, 4]
var sex:[String] = ["Male", "Male", "Female", "Male"]
//descending order array of indexes
let sortedOrder = points.enumerate().sort({$0.1>$1.1}).map({$0.0})
//Map the arrays based on the new sortedOrder
points = sortedOrder.map({points[$0]})
people = sortedOrder.map({people[$0]})
peopleIds = sortedOrder.map({peopleIds[$0]})
sex = sortedOrder.map({sex[$0]})
I just tested this solution out and it works well.
here is the solution with your own data.
let points:[Int] = [200, 1000, 100, 500]
let people:[String] = ["Harry", "Jerry", "Hannah", "John"]
let peopleIds:[Int] = [1, 2, 3, 4]
let sex:[String] = ["Male", "Male", "Female", "Male"]
let sortedPointsEnumerateIndexes = points.enumerate().sort { $0.element > $1.element}.map { $0.index }
var sortedPoints:[Int] = []
var sortedPeople:[String] = []
var sortedPeopleIds:[Int] = []
var sortedSex:[String] = []
for i in sortedPointsEnumerateIndexes {
sortedPoints.append(points[i])
sortedPeople.append(people[i])
sortedPeopleIds.append(peopleIds[i])
sortedSex.append(sex[i])
}
print(sortedPoints) // [1000, 500, 200, 100]
print(sortedPeople) // ["Jerry", "John", "Harry", "Hannah"]
print(sortedPeopleIds) // [2, 4, 1, 3]
print(sortedSex) // ["Male", "Male", "Male", "Female"]
// with map
let sorted2Point = sortedPointsEnumerateIndexes.map{ points[$0] }
let sorted2People = sortedPointsEnumerateIndexes.map{ people[$0] }
// ....
print(sorted2Point) // [1000, 500, 200, 100]
print(sorted2People) // ["Jerry", "John", "Harry", "Hannah"]

Resources