cocoafrog
(Joachim Kurz)
1
If I just want to get the non-nil elements of an optional collection (and fix it’s type to be of non-optional elements) it seems the best way is
let x: [Int?] = [1,2,3,nil,5]
let y = x.compactMap { $0 }
Why isn’t the default parameter of compactMap the closure { return $0 } so we could just call compactMap() instead?
3 Likes
shpakovski
(Vadim Shpakovski)
2
I find it a brilliant idea, +1 
1 Like
Tino
(Tino)
3
Afair there have been plans to add compact() — don't know why it is not here yet, though.
1 Like
millenomi
(Aura Lily Vulcano)
4
Certainly map in the name indicates the application of a transform closure, and the name for this operation, if it doesn't take a closure, likely ought to be different.
5 Likes
cocoafrog
(Joachim Kurz)
5
That's a very good point that I did not consider. But e.g. compact() (or something similar) still does not exist, or does it?
It seems like there must have been a discussion before or maybe even a reason why it shouldn't exist or why it's not necessary (e.g. because there is a better way).
duan
(Daniel Duan)
6
IMO a slightly more elegant solution is to have a func id<T>(_ v: T) -> T { v } in the standard library which enable us to write blah.compactMap(id). This id/identity function can further compose with operators in ReactiveX libraries, for example.
2 Likes
cocoafrog
(Joachim Kurz)
7
with SE-0249 we should be able to use the keypath \.self in this way
blah.compactMap(\.self)
But that still feels like more work than necessary.
3 Likes
compact() would strip out nulls from a container. compacted() would return a new container that's been stripped. IMO.
6 Likes
You’re proposing a method called compactMap() that doesn’t actually map anything, merely filters out nils.
If compactMap had never existed, being composible instead from map and some sansNils, then you would just use sansNils in isolation for this purpose.
The existence of compactMap is a bit of a mistake, I think - one people largely accept because it’s still fairly practical. But making it worse would be, well, even worse.
Sidenote: I don’t like the use of the term compact in any of this, because it’s way too ambiguous - it doesn’t say to me “removes nil“ specifically. Why doesn’t it mean to reallocate the underlying memory as one contiguous block? Or switch the internal representation from a linked list to an array? Or resize the backing memory to exactly the size of the current contents? Or apply bzip2 compression? Or reduce the type of the collection’s contents to the smallest type that still holds all the values (e.g. Int -> Int8)? Or remove duplicates? All of those are forms of compaction. It seems it only exists because somehow it got established as convention - some kind of chicken-and-egg paradox.
3 Likes
pyrtsa
(Pyry Jahkola)
10
It's totally possible to implement compact() as shown e.g. in SE-0218 — Introduce compactMapValues to Dictionary - #20 by pyrtsa.
But in absense of parameterised generics (something like extension<T> Sequence where Element == T? { ... }), I think overeager autocompletion might still be the issue preventing the adoption of this somewhat hacky implementation in the stdlib; we don't want to suggest xs.compact() when xs is not a sequence of optionals.
2 Likes
I was just thinking this a few days ago... Seemed strange. :)
IIRC we don't have compact because we don't have higher-kinded-types in Swift yet. If you use compact on a Set you want that the result remains a Set and is not transformed to an Array.
The best workaround right now is as already mentioned above compactMap(\.self), but since the proposal that enabled that feature didn't land yet you may need to create a custom operator for the time being and write compactMap(^\.self).
1 Like
since the proposal that enabled that feature didn't land yet
It's available on master.
3 Likes
Oh so it got recently merged after all. Thank you for mentioning that.
1 Like