Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get the count of a type conforming to `Sequence`?

Let's say I have this code:

func work<S: Sequence>(sequence: S) {
    // do stuff
}

How could I figure out how many elements there are in sequence?
The obvious version I'd go for is pretty inefficient:

var count = 0
for element in sequence {
    count += 1
}

There must be a nicer way, right?

like image 396
Marcus Rossel Avatar asked Feb 26 '15 21:02

Marcus Rossel


People also ask

How to conform to Sequence?

To add Sequence conformance to your own custom type, add a makeIterator() method that returns an iterator. Alternatively, if your type can act as its own iterator, implementing the requirements of the IteratorProtocol protocol and declaring conformance to both Sequence and IteratorProtocol are sufficient.

What is sequence protocol?

A sequence protocol requires a factory method that returns an Iterator type as shown in the code below: An iterator is a type that conforms to Iterator protocol. The protocol has a mutating method next() that returns individual values one after the other until it can't anymore, in which case it returns a nil.


2 Answers

I do not think that there is a better method for an arbitrary type conforming to SequenceType. The only thing that is known about a sequence is that is has a generate() method returning a GeneratorType, which in turn has a next() method. The next() method advances to the next element of the sequence and returns it, or returns nil if there is no next element.

Note that it is not required at all that next() eventually returns nil: a sequence may have "infinite" elements.

Therefore enumerating the sequence is the only method to count its elements. But this need not terminate. Therefore the answer could also be: A function taking a sequence argument should not need to know the total number of elements.

For types conforming to CollectionType you can use the countElements() function (renamed to count() in Swift 1.2).

There is also underestimateCount():

/// Return an underestimate of the number of elements in the given
/// sequence, without consuming the sequence.  For Sequences that are
/// actually Collections, this will return countElements(x)
func underestimateCount<T : SequenceType>(x: T) -> Int

but that does not necessarily return the exact number of elements.

like image 57
Martin R Avatar answered Oct 20 '22 19:10

Martin R


A more "functional" approach would be:

let count = sequence.reduce(0) { acc, row in acc + 1 }

Or even make an extension:

extension Sequence {
    var count: Int { return reduce(0) { acc, row in acc + 1 } }
}

let count = sequence.count

Note This does still suffer from the basic sequence problem that you have to consume the sequence to count it.

like image 29
David Berry Avatar answered Oct 20 '22 18:10

David Berry