Reputation: 2809
I have a Person() class:
class Person : NSObject {
var firstName : String
var lastName : String
var imageFor : UIImage?
var isManager : Bool?
init (firstName : String, lastName: String, isManager : Bool) {
self.firstName = firstName
self.lastName = lastName
self.isManager = isManager
}
}
I have an array of Person()
var peopleArray = [Person]()
I want to count the number of people in the array who have
isManager: true
I feel this is out there, but I can;t find it, or find the search parameters.
Thanks.
Upvotes: 28
Views: 15663
Reputation: 92439
With Swift 6, you can use Array
's count(where:)
method if you want to count the number of elements in an array that match a given predicate. count(where:)
has the following declaration:
func count<E>(where predicate: (Self.Element) throws(E) -> Bool) throws(E) -> Int where E : Error
Returns the number of elements in the sequence that satisfy the given predicate.
The following Playground sample code shows how to use count(where:)
:
struct Person {
let name: String
let isManager: Bool
}
let array = [
Person(name: "Jane", isManager: true),
Person(name: "Bob", isManager: false),
Person(name: "Joe", isManager: true),
Person(name: "Jill", isManager: true),
Person(name: "Ted", isManager: false)
]
let managerCount = array.count(where: { (person: Person) -> Bool in
return person.isManager
})
// let managerCount = array.count { $0.isManager } // also works
// let managerCount = array.count(where: \.isManager) // also works
print(managerCount) // prints: 3
Upvotes: 10
Reputation: 119310
You can use the following from Swift 6.0
peopleArray.count(where: \.isManager!)
You can get the above extension which is proposed to and accepted by the Swift team as the following:
extension Sequence {
@inlinable
public func count(
where predicate: (Element) throws -> Bool
) rethrows -> Int {
var count = 0
for e in self {
if try predicate(e) {
count += 1
}
}
return count
}
}
lazy
, reduce
and loop
methodCollection.count
.
count(where:)
function to something else to overcome this issue.You just need to do what you asked (in reversed order of saying):
/* Number of -> items with specific property -> in an array */
/* count -> .filter(\.isManager!) -> peopleArray */
// Just reversed of what you say in English:
peopleArray.filter(\.isManager!).count
A. NO, Array<Element>
conforms to RandomAccessCollection
. So the count
will NOT actually count the elements. It just returns the result (the endIndex
) with O(1) complexity.
A. YES, Swift Array
is copy on write, but calling filter
actually manipulates the array, so you will eventually get a new array, even if you don't assign it to a new variable.
A. Using lazy
subsystem or manually reduce
like:
peopleArray.lazy.filter(\.isManager!).count
It's perfectly safe and perfomant to call lazy->filter->count
here. The most important point here is no do NOT optimize prematurely because
Premature optimization is the root of all evil.
Tony Hoare
count(where:)
extension on the Sequence
: herelazy
, filter
etc.: hereloop
, reduce
and inline reduce
: hereUpvotes: 0
Reputation: 7944
Use filter
method:
let managersCount = peopleArray.filter { (person : Person) -> Bool in
return person.isManager!
}.count
or even simpler:
let moreCount = peopleArray.filter{ $0.isManager! }.count
Upvotes: 43
Reputation: 72760
You can use reduce
as follows:
let count = peopleArray.reduce(0, combine: { (count: Int, instance: Person) -> Int in
return count + (instance.isManager! ? 1 : 0) }
)
or a more compact version:
let count = peopleArray.reduce(0) { $0 + ($1.isManager! ? 1 : 0) }
reduce
applies the closure (2nd parameter) to each element of the array, passing the value obtained for the previous element (or the initial value, which is the 0
value passed as its first parameter) and the current array element. In the closure you return count
plus zero or one, depending on whether the isManager property is true
or not.
More info about reduce
and filter
in the standard library reference
Upvotes: 18