Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 353
Jignesh Fadadu Avatar asked Apr 18 '17 11:04

Jignesh Fadadu


People also ask

What is extensions in Swift?

Extensions add new functionality to an existing class, structure, enumeration, or protocol type. This includes the ability to extend types for which you don't have access to the original source code (known as retroactive modeling). Extensions are similar to categories in Objective-C.

How do you write an array extension in Swift?

For example: var myArray = [Foo]() means that myArray will only contain type Foo . Foo in this case is "mapped" to the generic placeholder Element . If you want to change the general behavior of Array (via extension) you would use the generic placeholder Element and not any concrete type (like Foo).

Why extension in Swift?

In Swift, we can add new functionality to existing types. We can achieve this using an extension. Here, we have created an extension of the Temperature class using the extension keyword. Now, inside the extension, we can add new functionality to Temperature .


2 Answers

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.

like image 187
BHendricks Avatar answered Oct 02 '22 14:10

BHendricks


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.

like image 41
farzadshbfn Avatar answered Oct 02 '22 15:10

farzadshbfn