Twitter khuong291
Twitter khuong291

Reputation: 11692

Swift Functional Programming

I have an String array:

let animals = ["Cat", "Chicken", "Fish", "Dog", "Mouse", "Pig", "Monkey"]

I want to get the result like this:

[(C, ["Cat", "Chicken"]),
 (D, ["Dog"]),
 (F, ["Fish"]),
 (M, ["Mouse", "Monkey"]),
 (P, ["Pig"])]

I have tried:

let animals = ["Cat", "Chicken", "Fish", "Dog", "Mouse", "Pig", "Monkey"]
typealias Entry = (Character, [String])

func buildIndex(words: [String]) -> [Entry] {
    let distinctLetters = Array(Set(words.map { Character($0.substringToIndex($0.startIndex.advancedBy(1))) })).sort()
    return distinctLetters.map({ (letter) -> Entry in
        return (letter, words.filter({ (word) -> Bool in
            Character(word.substringToIndex(word.startIndex.advancedBy(1))) == letter
        }))
    })
}
print(buildIndex(animals))

//[("C", ["Cat", "Chicken"]), ("D", ["Dog"]), ("F", ["Fish"]), ("M", ["Mouse", "Monkey"]), ("P", ["Pig"])]

Now I want to use it with $ syntax.

I have tried:

func buildIndex(words: [String]) -> [Entry] {
    let distinctLetters = Array(Set(words.map { Character($0.substringToIndex($0.startIndex.advancedBy(1))) })).sort()
    return distinctLetters.map{ ($0, words.filter {
        Character($0.substringToIndex($0.startIndex.advancedBy(1))) == $0 // I want to get $0 from the map, not from the filter
    })}  
}

enter image description here

But it says:

overloads for '==' exist with these partially matching parameter lists: (Character, Character), (String, String) Character($0.substringToIndex($0.startIndex.advancedBy(1))) == $0

I have also tried:

func buildIndex(words: [String]) -> [Entry] {
    let distinctLetters = Array(Set(words.map { Character($0.substringToIndex($0.startIndex.advancedBy(1))) })).sort()
    return distinctLetters.map { ($0, words.filter { distinctLetters.contains(Character($0.substringToIndex($0.startIndex.advancedBy(1)))) })}
}

But It doesn't work well. It prints:

[("C", ["Cat", "Chicken", "Fish", "Dog", "Mouse", "Pig", "Monkey"]), ("D", ["Cat", "Chicken", "Fish", "Dog", "Mouse", "Pig", "Monkey"]), ("F", ["Cat", "Chicken", "Fish", "Dog", "Mouse", "Pig", "Monkey"]), ("M", ["Cat", "Chicken", "Fish", "Dog", "Mouse", "Pig", "Monkey"]), ("P", ["Cat", "Chicken", "Fish", "Dog", "Mouse", "Pig", "Monkey"])]

So how to achieve this with $ systax.

Any helps would be appreciated. Thanks.

Upvotes: 1

Views: 231

Answers (3)

webcpu
webcpu

Reputation: 3422

If you want to use functional programming, you should focus on what to do but not how to do, and it also should be declarative. My solution is just for inspiration.

https://github.com/unchartedworks/HaskellSwift

import HaskellSwift

func test() {
    let animals                                   = ["Cat", "Chicken", "Fish", "Dog", "Mouse", "Pig", "Monkey"]
    let hasFirstLetter: (String, String) -> Bool  = { $0.first == $1.first }
    let pair: ([String]) -> (Character, [String]) = { ($0.first?.first ?? Character(""), $0)}
    let categorize                                = map(pair) .. curry(groupBy)(hasFirstLetter) .. sort
    let results                                   = categorize(animals)
    print(results)
}

Upvotes: 0

Michal
Michal

Reputation: 15669

What about doing it this way:

func buildIndex2(words: [String]) -> [Entry] {
    var res = [Character:[String]]()
    words.map({ res[$0[0]] = (res[$0[0]] ?? []) + [$0] })
    return res.sort{ $0.0 < $1.0 }
}

or even more $-way (in my opinion, less readable, though):

func buildIndex3(words: [String]) -> [Entry] {
    return animals.reduce([Character:[String]]()) {
        var res = $0
        res[$1[0]] = ($0[$1[0]] ?? []) + [$1]
        return res
    }.sort{ $0.0 < $1.0 }
}

String extension:

extension String {
    subscript (i: Int) -> Character {
        return self[self.startIndex.advancedBy(i)]
    }
}

The advantage in this solution is that you don't have to run filter for every letter you get in each step of the last map.

Upvotes: 1

Igor B.
Igor B.

Reputation: 2229

Consider this:

let animals = ["Cat", "Chicken", "Fish", "Dog", "Mouse", "Pig", "Monkey"]

let catalog = Array(Set(animals.map( {(ch) -> Character in ch.characters.first! })))
    .sort()
    .map({ (ch) -> (Character, [String]) in
        (ch, animals.filter { (name) -> Bool in name.hasPrefix(String(ch)) })
    })

print(catalog)
//[("C", ["Cat", "Chicken"]), ("D", ["Dog"]), ("F", ["Fish"]), ("M", ["Mouse", "Monkey"]), ("P", ["Pig"])]

Upvotes: 1

Related Questions