Reputation: 11692
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
})}
}
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
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
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
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