I'm trying to make an extension to Array, see below
extension Array {
func mapThenUnique<T: Comparable>(f: (Element) -> T) -> Array<T> {
var mapped: Array<T> = Array(self.filter( f(self) ))
/* Three Errors /\
1) Cannot assign value of type '[Element]' to type 'Array<T>'
2) Cannot convert value of type 'Array<Element>' to expected argument type 'Element'
3) Cannot convert value of type 'T' to expected argument type '(Element) throws -> Bool' */
var noDups: Array<T> = []
for element in mapped {
if (!noDups.contains(where: element)) { noDups.append(element) }
// Error - Cannot convert value of type 'T' to expected argument type '(T) throws -> Bool'
}
return noDups
}
}
I know the noDups array has to be of type 'T' because that is the correct return type, see below for an example of what mapThenUnique does:
["abc", "Hi", "AbC"].mapThenUnique { $0.lowercased() } -> ["abc", "hi"]
[2, 9, -9, 3].mapThenUnique { Int($0) * $0 } -> [4, 9, 81]
// notice that there are no duplicates in the array
But I'm not sure why I am getting these error messages. And what does '(Element) throws -> Bool' mean? Any help will be greatly appreciated!
Edit: My now awake brain realizes that it should be map instead of filter, thanks @Sulthan.
extension Array {
func mapThenUnique<T: Comparable>(f: (Element) -> T) -> Array<T> {
var mapped: Array<T> = self.map{ f($0) }
var noDups: Array<T> = []
for element in filtered {
if (!noDups.contains(where: element)) { noDups.append(element) }
// Error - Cannot convert value of type 'T' to expected argument type '(T) throws -> Bool'
}
noDups.sort()
// this works for the numerical test cases but not for ["abc", "Hi", "AbC"] which returns ["abc", "hi"], idk why it does
return noDups
}
}
I'm still trying to figure out what the error message in the for loop means. Oh, this is for a HW assignment.
CodePudding user response:
If you use a Set and make it Hashable instead of Comparable, it's fairly straightforward
extension Array {
func mapThenUnique<T: Hashable>(f: (Element) -> T) -> [T] {
var seen = Set<T>()
return map { f($0) }.filter { seen.insert($0).inserted }
}
}
["abc", "Hi", "AbC"].mapThenUnique { $0.lowercased() } // ["abc", "hi"]
[2, 9, -9, 3].mapThenUnique { Int($0) * $0 } // [4, 81, 9]
Or if you don't care about the original order
Set(["abc", "Hi", "AbC"].map { $0.lowercased() }) // ["abc", "hi"] or ["hi", "abc"]
Set([2, 9, -9, 3].map { Int($0) * $0 }) // Some random order of [4, 81, 9]
Also a more "correct" way to do this in Swift would be to create the extension on Collection
extension Collection where Element: Hashable {
func mapThenUnique(f: (Element) -> Element) -> [Element] {
var seen = Set<Element>()
return map { f($0) }.filter { seen.insert($0).inserted }
}
}
Although I consider it "bad form" to have a function that does two things, so my personal preference would be
extension Collection where Element: Hashable {
func unique() -> [Element] {
var seen = Set<Element>()
return filter { seen.insert($0).inserted }
}
}
["abc", "Hi", "AbC"].map { $0.lowercased() }.unique()
(Again, assuming you want to keep the order. Otherwise, just use a Set!)