-1

I have a dictionary with this structure:

a: [1,2]
b: [3,4]
c: [5,6]

and I need to return a string with this structure.

a,b,c\n1,3,5\n2,4,6

I solved the first part of the string. But to get the rest of the String. I try to iterate into my dictionary to get the first elements for each key in my dictionary and then get the rest for each value into the array.

Is there an easier way to get this?

Josué H.
  • 1,171
  • 3
  • 15
  • 35
  • 2
    A dictionary is an unordered collection. There is no way to maintain the elements order of the resulting string. If you want the dictionary keys in lexicographic order please add it to your post – Leo Dabus Dec 11 '20 at 16:09
  • Is any order in your dictionary key? like a,b,c key is any order. so, we can set order according to them. – Raja Kishan Dec 11 '20 at 16:12
  • 1
    Maybe you could add the code you have so far? – Joakim Danielson Dec 11 '20 at 16:18
  • Are the array always of the same size? If not, what's the final output? – Larme Dec 11 '20 at 16:28
  • My idea: Get the keys in the order you need. Get an array of its values according to the keys order. Then you have a Matrix. Look for a Transpose Matrix https://stackoverflow.com/questions/45412684/how-to-transpose-a-matrix-of-unequal-array-length-in-swift-3 https://stackoverflow.com/questions/32920002/how-to-transpose-an-array-of-strings etc. Then a few `join(separator:)` with `,` and `\n` should do the trick. – Larme Dec 11 '20 at 16:32

3 Answers3

1

Once you know what's the order of the keys (alpha ?), you can use this:

let dict: [String: [Int]] = ["a": [1,2], "b": [3, 4], "c": [5, 6]]
let keys = dict.keys.sorted() //Or do whatever you want here to get your target order
var matrix: [[String]] = []
keys.forEach {
    guard let arrayAsInt = dict[$0] else { return }
    let arrayAsString = arrayAsInt.map{ "\($0)" }
    matrix.append( [$0] + arrayAsString)
}
print("Matrix: \(matrix)")
let transposed = matrix.transposed()
print("Transposed Matrix: \(transposed)")
let output = transposed.map { $0.joined(separator: ",")}.joined(separator: "\n")
print(output)

The outputs:

$>Matrix: [["a", "1", "2"], ["b", "3", "4"], ["c", "5", "6"]]
$>Transposed Matrix: [["a", "b", "c"], ["1", "3", "5"], ["2", "4", "6"]]
$>a,b,c
1,3,5
2,4,6

Obvisouly the "\n" might be invisible and be an actual new line

a,b,c
1,3,5
2,4,6

Being

a,b,c\n1,3,5\n2,4,6

What's the idea behind that? Create a matrix and use the transpose (it's used in maths with matrix, it's one of the basic modification of a matrix).

First transform the [String: [Int]] into a [[String]], where each element would be key followed by its values. I transformed it there as String for simpler code later.

Why doing that? Because the matrix value is easy to get from your initial dict. the transposed value is harder (not impossible) to get from dict but easier from matrix, and the transposed is quickly transformed into your format.
So my thinking was the reverse: Get a structure from your output, then how to get it, it's a transpose, so I need to get the initial input as it, etc.

With the help of a code for Transpose Matrix (that accept String elements).

extension Collection where Self.Iterator.Element: RandomAccessCollection {
    // PRECONDITION: `self` must be rectangular, i.e. every row has equal size.
    func transposed() -> [[Self.Iterator.Element.Iterator.Element]] {
        guard let firstRow = self.first else { return [] }
        return firstRow.indices.map { index in
            self.map{ $0[index] }
        }
    }
}

Any code (there a various) working ones, should the trick. I took it from here.

As pointed by @Leo Dabus, you can remove the Self.Iterator.Element from the extension code (twice). I just wanted to it as such, not modifying the initial answer since it's not mind.

Larme
  • 24,190
  • 6
  • 51
  • 81
  • this is pretty old syntax `extension Collection where Element: RandomAccessCollection {` and `func transposed() -> [[Element.Element]] {` – Leo Dabus Dec 11 '20 at 16:57
  • Good point, I edited my answer. I wanted to keep the initial code as such. – Larme Dec 11 '20 at 17:00
  • 2
    Regarding your matrix code, the guard will never fail. BTW You can simplify your code as `let matrix = dict.keys.sorted().map { CollectionOfOne($0) + dict[$0, default: []].map(String.init) }` – Leo Dabus Dec 11 '20 at 17:49
1

What you are looking for, besides composing the final string, is how to transpose a collection (this would work with collections of different sizes as well):

extension Sequence {
    func max<T: Comparable>(_ predicate: (Element) -> T)  -> Element? {
        self.max(by: { predicate($0) < predicate($1) })
    }
}

extension Collection where Element: RandomAccessCollection, Element.Indices == Range<Int> {
    func transposed() -> [[Element.Element]] {
        (0..<(max(\.count)?.count ?? .zero)).map {
            index in compactMap { $0.indices ~= index ? $0[index] : nil }
        }
    }
}

let dict = ["a": [1,2,3],
            "b": [4,5,6],
            "c": [7,8,9]]

let sorted = dict.sorted(by: {$0.key < $1.key})
let result = sorted.map(\.key).joined(separator: ",") + "\n" +
             sorted.map(\.value).transposed().map {
                 $0.map(String.init).joined(separator: ",")
             }.joined(separator: "\n")

result // "a,b,c\n1,4,7\n2,5,8\n3,6,9"
Leo Dabus
  • 229,809
  • 59
  • 489
  • 571
0

A dictionary is an unordered collection so you need to sort it according to any specific key. Here I sort the dictionary according to the key if you don't care about an order you can just remove sort.

let dict: [String: Any] = ["a": [1,2], "b": [3,4], "c": [5,6]]

let sortedKey = dict.keys.sorted(by: <)
let key = sortedKey.joined(separator: ",")

var firstValues: [String] = []
var secondValues: [String] = []

sortedKey.forEach { (key) in
    if let dictValue = dict[key] as? [Int],
       let firstValue = dictValue.first,
       let secondValue = dictValue.last {
        
        firstValues.append("\(firstValue)")
        secondValues.append("\(secondValue)")
    }
}

let finalString = key + "\n" + firstValues.joined(separator: ",") + "\n" + secondValues.joined(separator: ",")

print(finalString) // "a,b,c\n1,3,5\n2,4,6"

Raja Kishan
  • 16,767
  • 2
  • 26
  • 52
  • Why not simply `.sorted()`? Btw `as? [Int]` is pointless. No need to explicitly set the dictionary values type to `Any`. `let dict = ...` – Leo Dabus Dec 11 '20 at 16:40
  • You can also use that but when we used sign then everyone can understand sort order quickly even if other programming developer and non-program. – Raja Kishan Dec 11 '20 at 16:45
  • This is also limited to collections of 2 elements. Why would you suppose that OP would always have only two elements in each array? – Leo Dabus Dec 11 '20 at 16:46
  • Do you want me to post how you should transpose an array? – Leo Dabus Dec 11 '20 at 16:49
  • In question it’s give an examples of how dictionary will comes and no anything mention in description regarding dynamic collection then it’s means it’s only two elements. And I don’t want anything from you. – Raja Kishan Dec 11 '20 at 17:11