1

I've got an array of fonts which each have a familyName and a fontName.

I would like to transform them into an array of tuples in the form (familyName: String, fontNames: [String]).

I feel like there should be an easy functional way to do this, but can't work it out. The closest I've got is two calls to reduce: First into a dictionary and then into an array.

let dictionary = fonts.reduce(into [String : [String]]() ) { result, font in
    let array = result[font.fontFamily] ?? []
    result[fontFamily] = array + [font.fontName]
}

let array = dictionary(into: [(String, [String])]() ) { result, element in
    result.append( (element.key, element.value.sorted()) )
}.sorted { $0.0 < $1.0 }

I'm also sorting the array of tuples and the array of fontNames in the array of tuples.

Is there a way I can avoid the intermediary dictionary?

Many thanks.

Update I created a playground to show sanjaykmwt the results of their suggestions:

struct Font {
    let family: String
    let name: String
}

let fonts = [
    Font(family: "ABC", name: "abc"),
    Font(family: "ABC", name: "def"),
    Font(family: "ABC", name: "ghi"),
    Font(family: "XYZ", name: "xyz"),
    Font(family: "XYZ", name: "uvw")
]

let sortedFamily = fonts.sorted(by: { (lhs, rhs) -> Bool in
    return lhs.family < rhs.family
})

let dict = sortedFamily.map({["family":$0.family,
                              "fonts":$0.name]})

print("dict: \(dict)")

Output:

dict: [["family": "ABC", "fonts": "abc"], ["family": "ABC", "fonts": "def"], ["family": "ABC", "fonts": "ghi"], ["family": "XYZ", "fonts": "xyz"], ["family": "XYZ", "fonts": "uvw"]]
2
  • you fonts is struct and you have an array of struct Commented May 24, 2018 at 9:40
  • I have given the shortest possible answer Commented May 24, 2018 at 10:05

3 Answers 3

2

if You have an array of Fonts with fontFamily, fontName

you can make grouping then map

 // Array Of Fonts Example

        let array = [Font.init(fontFamily: "Cago", fontName: "AA"),
                                     Font.init(fontFamily: "Cago", fontName: "CCCC"),
                                     Font.init(fontFamily: "Mango", fontName: "AAsss"),
                                      Font.init(fontFamily: "Mango", fontName: "mngoo")]

         // Grouping

        let groupedByFamilayName = Dictionary.init(grouping: array) {$0.fontFamily}

        // Map
        let arrayOfTuple =  groupedByFamilayName.map { (key,array) -> (String,[String]) in
                            return (key,array.map({$0.fontName}))
                        }

     print(arrayOfTuple)
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks. I didn't know about Dictionary.init(grouping: array)
2

Expanding (or contracting!) on Abdelahad Darwish's answer…

let tuples = Dictionary(grouping: fonts) { $0.family }
    .map { (familyName: $0.key, fontNames: $0.value.map { $0.name }) }

print(tuples)
[(familyName: "XYZ", fontNames: ["xyz", "uvw"]), (familyName: "ABC", fontNames: ["abc", "def", "ghi"])]

2 Comments

Thanks, this is approximately what I have now, plus a bit of dealing with optionals that I forgot to mention.
Nice coding Ashley
-2
    let sortedFamily = fonts.sorted(by: { (lhs, rhs) -> Bool in
        return lhs.family < rhs.family
    })

    let dict = sortedFamily.map({["family":$0.family,"fonts":$0.fonts.sorted()]})

try and print the dict you will get everything sorted

if you want even shorter it can be:

  let dict = fonts.sorted(by: { (lhs, rhs) -> Bool in
        return lhs.family < rhs.family
    }).map({["family":$0.family,"fonts":$0.fonts.sorted()]})

6 Comments

I think you have misunderstood the question. The question is about the transform of the data structure, not the sorting.
this gives you both the things you want if you only want to transform then you can use this instead: let dict = fonts.map({["family":$0.family,"fonts":$0.fonts]})
You refer to this $0.fonts which doesn't exist.
for you it is $0.fontFamily and $0.fontName
That just produces an array of dictionaries, each with one "family" and one "fonts" (which is a single font). At no point in the code are you merging the fontNames into one array (per familyName).
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.