Haravikk
Haravikk

Reputation: 3290

Simplest Way to Define an Anonymous CollectionType in Swift

Okay, so I have a class that is a CollectionType that contains a set of results, and can be accessed like an array to get each one.

However, not all of the results are relevant in all cases, so sometimes only fetching the first element is necessary, while other code may want to call only the additional values. I'll try to illustrate this with an example (please excuse any basic mistakes):

class MyClass: CollectionType {
    typealias Index = Int
    let startIndex: Index = 0
    let endIndex: Index

    // Initialiser here (setting self.endIndex)

    func generate() -> IndexingGenerator<MyClass> {
        return IndexingGenerator(self) 
    }

    subscript(theIndex: Index) -> String { /* Get value at given index. */ }

    var first: String { return self[0] }
}

What I'd effectively like to do is create a computed property others which will itself behave as a collection, but omitting the first item in the main results object, so I can do something like this:

let first = myClassInstance.first
for item in myClassInstance.others { /* Do something */ }

I've simplified the example considerably, so no, I'm afraid I can't just make first a stored property or somehow break the results apart; the results are relevant both in their entirety, and also in the above separated format for different purposes, where I'd like to avoid code using this class having to be aware of which indexes they need (usually they will only want either the full set of results, or one of first or others)

So anyway, with this in mind, what would be the best way for me add my others computed property?

Upvotes: 1

Views: 89

Answers (1)

Airspeed Velocity
Airspeed Velocity

Reputation: 40963

If you’re happy with others just being a sequence (so would support for…in, map, filter etc), you could do something like this:

class MyClass: CollectionType {
    var others: SequenceOf<String> {
        return SequenceOf { ()->GeneratorOf<String> in
            var g = self.generate()
            // the following fiddlyness courtesy of the requirement
            // not to call .next() a second time after it returns nil...
            if g.next() != nil {
                return GeneratorOf { g.next() }
            }
            else {
                return GeneratorOf { nil }
            }
        }
    }
}

let mc = MyClass()
for item in mc.others {
    println(item)
}

Depending on how you are holding your internal data, you might be able to simplify that a lot by returning the generator of a slice of your internal data.

If you want something that returns a collection rather than just a sequence, consider making your collection conform to Sliceable. You could then use dropFirst(self).

P.S. for consistency with the rest of the Swift std lib, you should consider having first return an optional in case of an empty collection: var first: String? { return Swift.first(self) }. Even if your implementation guarantees at least one entry, the consistency may be worth it.

Upvotes: 1

Related Questions