Jignesh Fadadu
Jignesh Fadadu

Reputation: 849

Generic Extension for Array in Swift

To be very frank, I am totally new to learn Extension creation and usage.

I wanted to create a category (Extension in swift 3.0) which can be used throughout an application to perform repeated operations for Array.

Sample Link 1

This is what I have seen and understand while doing research, I wanted to create an extension with various methods which should be generic, and not on the basis of datatype needed to create separate extensions.

Here in above example, we will need to create single extension if we will go for particular datatype wise extension. I wanted to have a guidance if any way is there to create the generic category (Extension in swift).

  1. extension _ArrayType where Generator.Element == Int
  2. extension Array where Element: Equatable
  3. extension Array where Element == Int
  4. extension _ArrayType where Generator.Element == Float
  5. extension SequenceType where Self.Generator.Element: FloatingPointType
  6. extension Array where Element: DoubleValue
  7. extension Sequence where Iterator.Element == String

,etc...

Sample Link 2

Note : In short, we can consider that I want to perform actions based on Array in single extension instead of just creating the single extension for each of the datatypes as per above requirement.

Upvotes: 19

Views: 16693

Answers (2)

farzadshbfn
farzadshbfn

Reputation: 2748

In where clause, you specify "If the Element type has these rules, consider this extension".

You don't need to implement all of the methods in all extensions.

For example:

  • You want to extend Array<Element> to generally have method foo(_:Element):

    extension Array {
        func foo(bar: Element) { /*your code goes here */ }
    }
    
  • You want to extend Array<Element> where Element did implement Equatable (which includes Int,Double and ... or any structs/classes you've marked as Equatable):

    extension Array where Element: Equatable {
        func find(value: Element) -> Bool { 
            return index(of: value) != nil
        }
    }
    
  • You want to extend Sequence in cases that Element is Numeric, have get-only variable sum:

    extension Sequence where Element: Numeric {
        var sum: Element { 
            return reduce(0, +)
        }
    }
    
  • You want to extend Collection<Collection<Element: Equatable>> to have a method to compare to 2D Collections:

    extension Collection
        where Iterator.Element: Collection, 
        Iterator.Element.Iterator.Element: Equatable {
    
        func compare(to: Self) -> Bool {
            let flattenSelf = self.reduce([], +)
            let flattenTo = to.reduce([], +)
    
            return flattenSelf.count == flattenTo.count &&
                zip(flattenSelf, flattenTo).reduce(true) { $0 && $1.0 == $1.1 }
        }
    }
    

You don't need to extend Array or collection to have methods like sort, find, etc... Most of these methods are already extended inside the compiler if your Element: Equatable or Element: Comparable. using map, filter and reduce you can achieve more complex structures with not much of a code.

Upvotes: 18

BHendricks
BHendricks

Reputation: 4493

As mentioned in the comments, one way to accomplish this is to create your own protocol that the types you want to cover adopt (in the comments someone called it Content, used below for this example) (from first source):

protocol Content {
    var hash: String { get }
}
extension Array where Element : Content {

    func filterWithId(id : String) -> [Element] {
        return self.filter { (item) -> Bool in
            return item.id == id
        }
    }
}

It seems, though, that the original question is mainly asking about generic extensions for arrays, which one comment says are not possible but 100% are possible in Swift (it's a big Swift feature, actually) (from second source).

For example, if you want to define a specific extension method for Ints only, you can do that:

extension Sequence where Iterator.Element == Int {
    var sum: Int {
        return reduce(0, +)
    }
}

It seems like the question's original requirements are extension methods that could be agnostic to data type and therefore should be kept in common. If I understand correctly, seems though that these data types in general have some conformance to Equatable and/or Hashable, which is the minimum requirement for this kind of generic-stuff to work. With this element conformance, though, this is possible as such:

extension Sequence where Iterator.Element is Equatable {
    func extensionMethodName<T: Equatable>(_ input: [T], singleElement: T) -> [T] {
        // T is now a generic array of equatable items. You can implement whatever extension logic you need with these. 
        // I added different ways of passing in and returning this generic type, but the only thing that is likely going to be consistent is the `<T: Equatable>` which is Swift standard syntax for declaring generic type parameters for a method.
    }
}

The Swift syntax changes quickly, and what's here can quickly go out of date, but this guide is kept fairly up-to-date by Apple and shows the most up to date syntax for Generics used above ^.

My answer pulls from a couple StackOverflow questions/answers, used for example/syntax above ^. Source: (SO Source) (SO Source 2)

In summary, all the methods above can be combined, for a fully custom extension solution that has both generic functions/vars for all your Array types, while still having type-specific extension overrides.

Upvotes: 24

Related Questions