R Menke
R Menke

Reputation: 8391

CollectionType extension with return Type Self instead of Array

The CollectionType methods are all functional and the ones that return a collection all return an array.

Is it possible to create a generic function that implements a map / forEach like function but has a return type of Self?

extension CollectionType {

    func otherMap(block: Generator.Element -> Generator.Element) -> Self {

        var copy = self

        copy.forEach {
            $0 = block($0) // this obviously fails since $0 is immutable 
        }

        return copy
    }   
}

Update :

Can this be unified? (func name says mutate even when it doesn't, I know ;))

extension Set {

    func mutateCollection(block: Generator.Element -> Generator.Element) -> Set {

        var copy : Set<Element> = []

        for element in self {
            copy.insert(block(element))
        }

        return copy
    }
}

extension Dictionary {

    func mutateCollection(block: Generator.Element -> Generator.Element) -> Dictionary {

        var copy : [Key:Value] = [:]

        for keyValuePair in self {

            let key = keyValuePair.0
            let value = keyValuePair.1

            let blockResult = block(key, value)
            copy[blockResult.0] = blockResult.1

        }
        return copy
    }
}

extension Array {

    func mutateCollection(block: Generator.Element -> Generator.Element) -> Array {

        var copy : [Element] = []

        for element in self {
            copy.append(block(element))
        }
        return copy
    }
}

Upvotes: 1

Views: 391

Answers (1)

Rob Napier
Rob Napier

Reputation: 299355

There is no promise that an arbitrary CollectionType can be instantiated or copied. So there's no way to implement your extension. A good way to see this is to try to implement this for FlattenCollection or LazyCollection. Or try creating a RandomValueCollection and then try to implement this method.

But you're free to do this on a RangeReplaceableCollectionType, which makes all the promises you need:

extension RangeReplaceableCollectionType {
    func otherMap(block: Generator.Element -> Generator.Element) -> Self {
        var copy = Self.dynamicType.init()
        self.forEach {
            copy.append(block($0))
        }
        return copy
    }   
}

Not all collections conform to RangeReplaceableCollectionType. There probably is a good reason Set doesn't, so you may have to create a simpler protocol of your own.

Remember, a CollectionType may represent some static thing that isn't meaningful or safe to copy. For example, I might make a CollectionType that represents "the files on this disk." Copying it could invalidate invariants (such as assumptions about caches and file pointers). There's no promise that a CollectionType is a value type. It is free to be a reference.

Upvotes: 1

Related Questions