Latest Swift, compact map by case? - higher-order-functions

I'm doing this,
let x = results.compactMap {
if case .person (let P) = $0 { return P }
else { return nil }
}
x is [Person] , obviously results is something like
enum ResultItem: Decodable {
case person(Person)
case cat(Cat)
case budgie(Budgie)
Anyway I'm doing this,
let x = results.compactMap {
if case .person (let P) = $0 { return P }
else { return nil }
}
inevitably someone on here can make me look like a fool and show a tighter way to do this in current Swift!
Go for it...

So results is an array of ResultItem and the idea is to extract the payloads from only the ones that are Persons?
I don't know if this is "tighter" but I would probably do it with for case let:
let arr : [ResultItem] = // ...
var persons = [Person]()
for case let .person(p) in arr { persons.append(p) }
You might reasonably object: "But I don't want persons to be a var." Yes, I know how you feel. So sometimes I say this instead (wordier, to be sure):
let persons : [Person] =
arr.reduce(into: []) {if case let .person(p) = $1 {$0.append(p)}}
They are both really ways of saying the same thing: append only if you can extract a Person. Of course you are doing that same thing, in a way, but the roundabout return nil into a compactMap is probably the objectionable part, and the second formulation is arguably a one-liner, so perhaps that's "tighter".

maybe define
func person(_ r: ResultItem)->Person? {
if case .person (let P) = r { return P }
else { return nil }
}
and next compact. at least it is easier to read for me ...
let persons = items.compactMap(person)
or even better
extension ResultItem {
static func person(_ r: ResultItem)->Person? {
if case .person (let P) = r { return P }
else { return nil }
}
}
and
let persons = items.compactMap(ResultItem.person)

Related

Argument passed to call that takes no arguments.. Firebase

Not sure why i'm getting this.. any suggestions would be grateful!
I ran into issues with my original coding where I had Firebase pod and Firebase Package.. so I started from scratch since that wasnt fixing itself.. now I get this.. and I am at a loss for how to resolve it.
static func fetchUsers() -> AnyPublisher<[UserProfile], Error> {
Future< [UserProfile], Error > { promise in
self.db.collection("Users")
.getDocuments { (snapshot, error) in
if let error = error {
promise(.failure(error))
return
}
guard let snapshot = snapshot else {
promise(.failure(FirebaseError.badSnapshot))
return
}
var users = [UserProfile]()
snapshot.documents.forEach { document in
print(users.count)
if let user = try? document.data(as: UserProfile.self){
if users.contains(where: { $0.id == user.id}) {return}
users.append(user)
} else {
print("Not working")
}
}
promise(.success(users))
}
}
.eraseToAnyPublisher()
}
I believe this is the syntax you're after:
var users = [UserProfile]()
users = snapshot.documents.compactMap { (document) -> UserProfile? in
if users.contains(where: { $0.id == user.id}) {
return nil
} else {
return try? document.data(as: UserProfile.self)
}
}
Also be aware that when you iterate something in Swift and encounter a false condition on an iteration, return will return out of the greater scope, not just that iteration. Therefore, use continue.
for x in y {
guard x > 0 else {
continue // continues loop
}
// ...
}

How do I write a generic recursive function that takes a CollectionType in Swift?

How would you create a generic function to process a CollectionType in Swift? For example, I want something that boils down to this:
func f<C: CollectionType>(list: C) {
if list.isEmpty {
return
}
else {
f(list.dropFirst()) // causes error
}
}
This causes an error because SubSequence might not be a CollectionType: Cannot invoke 'f' with an argument list of type '(C.SubSequence)'
I tried working around it by constraining the SubSequence type like this:
<C: CollectionType where C.SubSequence: CollectionType>
I get the same error, though. Any suggestions on how to write a generic recursive function for CollectionType?
To fix your error, you can transform a SubSequence into an array with the Array initializer:
func f<C: CollectionType>(list: C) {
if list.isEmpty {
return
}
else {
f(Array(list.dropFirst()))
}
}
Here's a solution that doesn't require copying the storage in a new array with every iteration:
func f<C: CollectionType
where C.SubSequence: CollectionType,
C.SubSequence == C.SubSequence.SubSequence>(c: C) {
guard let first = c.first else { return }
print(first)
f(c.dropFirst())
}
In Swift 3:
func f<C>(_ c: C) where
C: Collection,
C.SubSequence: Collection,
C.SubSequence == C.SubSequence.SubSequence {
guard !c.isEmpty else { return }
f(c.dropFirst())
}
f([1, 2, 3])

Swift: filter protocol array by comparing types

(first post)
usually im able to find answers here or elsewhere but no luck this time =(
Question: in Swift, how do you filter an array that is of a protocol type by an implementing type supplied as a function parameter?
protocol Aprotocol {
var number:Int { get set }
}
class Aclass: Aprotocol {
var number = 1
}
class AnotherClass: Aprotocol {
var number = 1
}
var array:[Aprotocol] = [ Aclass(), AnotherClass(), Aclass() ]
func foo (parameter:Aprotocol) -> Int {
return array.filter({ /* p in p.self == parameter.self */ }).count
}
var bar:Aprotocol = // Aclass() or AnotherClass()
var result:Int = foo(bar) // should return 2 or 1, depending on bar type
maybe this is not the right approach at all?
thanks!
Here is what I think you want:
return array.filter { (element: Aprotocol) -> Bool in
element.dynamicType == parameter.dynamicType
}.count
But I recommend this, which does the same, but without the useless instance of Aclass() which is passed in the answer on the top. Also this way is faster:
func foo <T: Aprotocol>(type: T.Type) -> Int {
return array.filter { (element: Aprotocol) -> Bool in
element.dynamicType == type
}.count
}
var result:Int = foo(Aclass)
The dynamicType will return the Type of an instance
Very easy:
return array.filter({ parameter.number == $0.number }).count
Kametrixoms solution works (if you use "is T" instead of "== type") but in my case, since i didnt know which implementing class was going to call it, had to go with this solution:
protocol Aprotocol: AnyObject {
var number:Int { get set }
}
class func foo(parameter: AnyObject) -> Int {
return array.filter ({ (element: Aprotocol) -> Bool in
object_getClassName(element) == object_getClassName(parameter)
}).count
}

use of abstract as enumerated type?

I am looking at the following Haxe source code from the "Pattern Matching - Array Matching" example at try.haxe.org -
class Test {
static function main() {
var playerA = {
name: "Simn",
move: Move.Paper
}
var playerB = {
name: "Nicolas",
move: Move.Rock
}
// a switch can directly return something
var winner = switch ([playerA.move, playerB.move]) {
case [Move.Rock, Move.Paper]: playerB;
case [Move.Scissors, Move.Paper]: playerA;
default: null;
}
if (winner != null) {
trace('The winner is: ${winner.name}');
} else {
trace('Draw!');
}
}
}
#:enum
abstract Move(Int) {
var Rock = 1;
var Paper = 2;
var Scissors = 3;
}
my questions are:
what does the notation #:enum signify?
Why did they create the enumerated type this way instead of simply doing:
enum Move
{
Rock;
Paper;
Scissors;
}
The #enum metadata informs the compiler to treat an abstract class as an enumeration. The main advantage is to be able to define values to each constant.
In the case of the example, the array [Move.Rock, Move.Paper] would be the same as [1,2] but the compiler will ensure that each value comes from the enum type.
You can find more here
http://haxe.org/manual/types-abstract-enum.html

Swift - using enum in switch statement

I get this error:
'NSNumber' is not a subtype of Cat
Here is the code:
enum Cat:Int {
case Siamese = 0
case Tabby
case Fluffy
}
let cat = indexPath.row as Cat
switch cat {
case .Siamese:
//do something
break;
case .Tabby:
//do something else
break;
case .Fluffy:
break;
}
How can I resolve this error?
Use Cat.fromRaw(indexPath.row) to get the enumeration.
Because the return value of fromRaw() is an optional, use it like thus:
if let cat = Cat.fromRaw (indexPath.row) {
switch cat {
// ...
}
}
The way I handled this same kind of situation in a recent app was to use a Struct consisting entirely of static members, instead of an Enum - in part because I had more information to associate with each option, in part because I got sick of having to call toRaw() and fromRaw() everyplace, and in part because (as your example shows you've discovered) an Enum loses its advantage when it turns out that you can't cycle through, or get a complete list of, the cases.
So, what I did was this:
struct Sizes {
static let Easy = "Easy"
static let Normal = "Normal"
static let Hard = "Hard"
static func sizes () -> [String] {
return [Easy, Normal, Hard]
}
static func boardSize (s:String) -> (Int,Int) {
let d = [
Easy:(12,7),
Normal:(14,8),
Hard:(16,9)
]
return d[s]!
}
}
struct Styles {
static let Animals = "Animals"
static let Snacks = "Snacks"
static func styles () -> [String] {
return [Animals, Snacks]
}
static func pieces (s:String) -> (Int,Int) {
let d = [
Animals:(11,110),
Snacks:(21,210)
]
return d[s]!
}
}
Now when we get to cellForRowAtIndexPath I can talk like this:
let section = indexPath.section
let row = indexPath.row
switch section {
case 0:
cell.textLabel.text = Sizes.sizes()[row]
case 1:
cell.textLabel.text = Styles.styles()[row]
default:
cell.textLabel.text = "" // throwaway
}
Essentially I've just used the two Structs as namespaces with some added intelligence. I'm not saying this is better than what you're doing; they are both eminently Swifty. It's just another idea to consider.

Resources