19

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.

Community
  • 1
  • 1
Jignesh Fadadu
  • 849
  • 1
  • 10
  • 29
  • 2
    What are you trying to achieve? Which operations are you referring to? – averello Apr 18 '17 at 11:38
  • 1
    I wanted to create category(in term of Objective C) class for Array in swift, which will offer to perform all the operations using those category methods defined for various datatypes (i.e. wanted to create methods in to that extension, which will be applicable on array consist of int, double, float, dictionary, string etc...) any of the types. – Jignesh Fadadu Apr 18 '17 at 11:43
  • 2
    This sounds like just `extension Array { ... }`. What problem are you having with that? – Rob Napier Apr 18 '17 at 12:28
  • 1
    Suppose I have to pass any parameter to extension method and if that parameter will be dynamic type (i.e. int, double, float, dictionary, string etc...) then it should work with any type of the object. No need to create separate extension for each individual datatype. – Jignesh Fadadu Apr 18 '17 at 12:38
  • You cannot define a generic method that takes anything. You could define methods on an extension of Array that operate on a 'common kind' of types. Like a method that operates on integers and another that operates on floating point numbers. Check out if [map(_:)](https://developer.apple.com/reference/swift/array/1689321-map), [filter(_:)](https://developer.apple.com/reference/swift/sequence/1641239-filter) and [reduce(_:_:)](https://developer.apple.com/reference/swift/array/2298686-reduce) can help you before creating custom extensions. – averello Apr 18 '17 at 12:42
  • 1
    Here I have 5 different controller, from each i need to perform different operations like if from One Controller i need to sort array consist of integer in ascending or descending order. From Second Controller I need to sort array consist of dictionary value in ascending or descending order bases on any particular key parameter. From Third Controller I need to sort array consist of id(Any) type. *************** Same way if I want to create method for insert, remove, replace object with above mentioned datatypes dynamically then how can i be able to create generic method which can help me? – Jignesh Fadadu Apr 18 '17 at 12:45
  • 1
    I always prefer to create reusable custom classes or categories while doing development, which will help me as well as other persons too while going to deal with any new thing. – Jignesh Fadadu Apr 18 '17 at 12:48
  • I think you should wrap your integer,dictionary,id(any) in an enum 'Content' and make it Comparable. Otherwise, create a protocol Content that conforms to Comparable, and define in there the operations you want your Controllers to have. Then each of your controllers would have a 'Content' property (or an Array). – averello Apr 18 '17 at 12:50
  • 1
    Ok, then how will it help me while going to deal with operations like simple insert, insert at object, remove last or first object, remove all object, replace object, sort an array which contains of dictionary and on the basis of key i will need to sort it, contains, remove duplicate values etc... operations ? Same thing for array with rest datatypes. Can you please give any example for it? As i have told I am totally new to extension. Even new to Swift 3.0 also. – Jignesh Fadadu Apr 18 '17 at 12:57
  • 1
    Can you please update your question with what you're actually trying to do? – LinusGeffarth Apr 21 '17 at 06:58
  • I have already given all details in question and comments as well, can you please gone through it ? – Jignesh Fadadu Apr 21 '17 at 07:02
  • @JohnPatel It's not clear at all what you're asking. It would most helpful if you could [edit] your question with a concrete example of the problem you're trying to solve here. People shouldn't have to dig through the comments to understand a question. – Hamish Apr 22 '17 at 20:31
  • @JohnPatel What do you mean by "*From Third Controller I need to sort array consist of id(Any) type*"? You cannot possibly sort an array of *anything* – because what can you even sort it by? You don't know anything about the elements. Really, you probably didn't mean to have an `[Any]` in the first place. – Hamish Apr 22 '17 at 20:34
  • Most of the functions you've mentioned are already implemented inside `Array` if your `Element` is implementing `Equatable`... – farzadshbfn Apr 22 '17 at 21:32
  • @Hamish My friend my main concern over here to get a way to create such an extension which can be helpful to all the developers not only to me. – Jignesh Fadadu Apr 24 '17 at 05:19
  • That doesn't help clarify your question @JohnPatel. You need to focus on a *specific* problem that you're trying to solve here – as it currently stands, your question is too unclear. As was said above, why doesn't `extension Array { ... }` solve your problem? That is an extension "*which will be applicable on array consist of int, double, float, dictionary, string etc...) any of the types.*" – Hamish Apr 24 '17 at 09:19
  • If you "only" want to sort your arrays then there is a sort-method already, flexible enough to be amended with any comparison function required. However you seem to want to keep a *sorted* array (given your emphasis on methods like `insert`, `create` and `update` which *are* already present for arrays, but will *not* sort of course) this will turn out pretty tricky. Since Array is a `struct` you cannot extend it and can only add *methods* to it through `extension`, not *fields*. You would probably have to implement a generic class with a all necessary methods of `Array` and a `sort`-function. – Patru Apr 27 '17 at 10:06
  • @Patru, Thanks for the reply. That is what my confusion and question. Is there any specific way to sort out this issue. Because in current scenario we will need to deal with each and every type of data separately instead of any generic solution. Any help is really appreciable in advance. – Jignesh Fadadu Apr 27 '17 at 11:23
  • Not quite sure if I got that right, but if I understand your problem correctly then you *do* want to keep a *sorted* array? If that is what you do I would suggest you google for it. A quick look lead to [this blogpost](https://oleb.net/blog/2017/02/sorted-array/) which will lead you to a GitHub repo with a finished implementation. At a first glance he seems to use a struct comprising an array and a comparator function as I suggested before. Check it out to see if it suits your needs. – Patru Apr 27 '17 at 12:03
  • This one `extension Array where Iterator.Element == Int` helped me to make generic random picking of elements with my own random-logic. – sabiland Feb 18 '22 at 09:47

2 Answers2

21

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.

Community
  • 1
  • 1
BHendricks
  • 4,423
  • 6
  • 32
  • 59
17

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.

farzadshbfn
  • 2,710
  • 1
  • 18
  • 38