Swift Extension of Array with Equatable Elements Cannot Call Index(of:) - arrays

I am attempting to add an extension to the Array type in Swift limited to Arrays whose elements conform to the equatable protocol. I am attempting to define a function in the following manner:
import Foundation
extension Array where Iterator.Element: Equatable {
func deletedIndicies<T: Equatable>(newArray: [T]) -> [Int] {
var indicies = [Int]()
for element in self {
if newArray.index(of: element) == nil {
indicies.append(self.index(of: element)!)
}
}
return indicies
}
}
}
The purpose of the function is to return the indices of any items in the original array that do not appear in the newArray.
The error I receive in Xcode is: Cannot invoke 'index' with an argument list of type '(of: Element)'
Since I am defining the function for only Arrays whose elements are equatable and am requiring that the elements of the newArray are equatable, I am unsure why I cannot invoke the index method.

The problem is you're defining a new generic placeholder T in your method – which is not necessarily the same type as Element. Therefore when you say newArray.index(of: element), you're trying to pass an Element into a argument of type T.
The solution therefore is to simply to type the newArray: parameter as [Element]:
extension Array where Element : Equatable {
func deletedIndicies(byKeeping elementsToKeep: [Element]) -> [Int] {
// ...
}
}
As a side note, this method could also be implemented as:
extension Array where Element : Equatable {
func deletedIndicies(byKeeping elementsToKeep: [Element]) -> [Int] {
// use flatMap(_:) to iterate over a sequence of pairs of elements with indices,
// returning the index of the element, if elementsToKeep doesn't contains it,
// or nil otherwise, in which case flatMap(_:) will filter it out.
return self.enumerated().flatMap {
elementsToKeep.contains($1) ? nil : $0
}
}
}
Also, if you change the constraint on Element to Hashable, this could be also be implemented in O(n), rather than O(n * m) time, which could potentially be desirable:
extension Array where Element : Hashable {
func deletedIndicies(byKeeping elementsToKeep: [Element]) -> [Int] {
// create new set of elements to keep.
let setOfElementsToKeep = Set(elementsToKeep)
return self.enumerated().flatMap {
setOfElementsToKeep.contains($1) ? nil : $0
}
}
}

Related

Swift: extend protocol whose associated type is an array

I am trying to create an Rx operator that works on arrays. I've tried this simple extension:
extension ObservableType where Element == Array<Any> {
func beat<U>(_ beat: Observable<U>) -> Observable<Element.Element> {
let lhs = self.flatMap { Observable.from($0) }
return Observable.zip(lhs, beat).map { $0.0 }
}
}
Now, I've noted Array<Any> because the otherwise I get
Reference to generic type 'Array' requires arguments in <...>, but this way the return value is of courses Observable<Any>.
Is it even possible to return an observable with the actual Element of the array?
How about this?
func beat<U>(_ beat: Observable<[U]>) -> Observable<[U]> where [U] == Element {
Observable.zip(self, beat).map { $0.0 }
}
Most likely you want to use the parametrized extensions that is not implemented in Swift 5.2.
As a workaround, you can create a global generic function that accepts Array<T> as the second parameter.

Why popFirst throws an error, but removeFirst works?

struct Queue<T>{
private var elements : [T] = []
public mutating func enqueue(_ element: T){
elements.append(element)
}
public mutating func dequeue() -> T?{
return elements.popFirst() // ERROR!
}
public mutating func dequeue2() -> T?{
return elements.removeFirst()
}
}
The error I get for popFirst is:
cannot use mutating member on immutable value: 'self' is
immutable
Both popFirst and removeFirst are marked as mutating and both return and T?. So why isn't it working?
EDIT: As others have commented, it seems to be some sort of bug. It's been discussed in the forums here.
EDIT: Still happens in Xcode 9.4.1 (Swift 4.1.2)
The error is improved in Swift 4.2:
error: ios.playground:4:25: error: '[T]' requires the types '[T]' and 'ArraySlice<T>' be equivalent to use 'popFirst'
return elements.popFirst() // ERROR!
^
You get the error because popFirst is not defined for all Collections. It's only defined if the Collection is its own SubSequence type. Here's the implementation:
extension Collection where SubSequence == Self {
/// Removes and returns the first element of the collection.
///
/// - Returns: The first element of the collection if the collection is
/// not empty; otherwise, `nil`.
///
/// - Complexity: O(1)
#inlinable
public mutating func popFirst() -> Element? {
// TODO: swift-3-indexing-model - review the following
guard !isEmpty else { return nil }
let element = first!
self = self[index(after: startIndex)..<endIndex]
return element
}
}
The extension requires SubSequence == Self. Self (with a capital S) is the type of self (with a lower-case s). It is the type on which you're calling popFirst. In your code, Self is Array<T>, also spelled [T].
The constraint is necessary for this line of popFirst to compile:
self = self[index(after: startIndex)..<endIndex]
^__^ ^_______________________________________^
| |
| This is a SubSequence.
|
This is a Self.
self[index(after: startIndex)..<endIndex] is a SubSequence.
Swift can only assign a SubSequence to Self if it knows that Self == SubSequence.
Array's SubSequence type is ArraySlice. Since ArraySlice is not the same type as Array, this extension doesn't apply to Array.
Here's the extension you're probably looking for:
extension Array {
#inlinable
public mutating func popFirst() -> Element? {
if isEmpty {
return nil
} else {
return removeFirst()
}
}
}

Swift3: type inference inside generic extension

I need to do something like this:
extension Array {
func flat() -> Element { return self.flatMap { $0 } }
}
But there is problem with type inference:
'flatMap' produces '[SegmentOfResult.Iterator.Element]', not the
expected contextual result type 'Element'
Edit: Usage example:
[[1,2],[3,4,5],[6]].flat()
should produce [1,2,3,4,5,6] which is the same as:
[[1,2],[3,4,5],[6]].flatMap { $0 }
If you take a look at the flatMap(_:) signature,
extension Sequence {
// ...
public func flatMap<SegmentOfResult : Sequence>(_ transform: (Self.Iterator.Element) throws -> SegmentOfResult) rethrows -> [SegmentOfResult.Iterator.Element]
// ...
}
you'll see that it returns a [SegmentOfResult.Iterator.Element], where SegmentOfResult is the type that is returned from the function you pass it. This isn't necessarily the same type as Element (as your extension is for all arrays), which is why your code fails to compile.
In order to be working with arrays where the elements are sequences, you'll want to constrain your extension so that Element : Sequence.
Also, as the function you're passing to flatMap(_:) is an identity transform (it just returns the element it receives as input), you'll want to change the return type to [Element.Iterator.Element] (an array of the inner element).
extension Array where Element : Sequence {
func flat() -> [Element.Iterator.Element] {
return self.flatMap { $0 }
}
}
Although that being said, I see no reason why this shouldn't be an extension of Sequence:
// An extension for a sequence of sequences
extension Sequence where Iterator.Element : Sequence {
// returns an array of the inner element type (an array of the element of the element)
func flat() -> [Iterator.Element.Iterator.Element] {
return self.flatMap { $0 }
}
}
(However, I don't really see the need to create an extension for this in the first place – array.flatMap{$0} isn't exactly lengthy!)

How do I implement the filter function in Swift

To gain a better understanding of Swift, I've extended SequenceType to add my own versions of map, reduce and forEach functions. However, the filter function is different in that it returns an array of Self.Generator.Element. Here's what I have so far:
extension SequenceType {
public func myFilter(#noescape includeElement: (Self.Generator.Element) throws -> Bool) rethrows -> [Self.Generator.Element] {
var array = [Self.Generator.Element]()
for element in self {
do {
if try includeElement (element) {
array.append(element)
}
}
}
return array
}
}
The statement var array = [Self.Generator.Element]() produces an "Invalid use of () to call a value of non-function type [Self.Generator.Element.Type]" error. My question is how do I create/add to/return an array of Self.Generator.Element?
Not sure why, but Xcode is fine with following syntax:
extension SequenceType {
public func myFilter(#noescape includeElement: (Self.Generator.Element) throws -> Bool) rethrows -> [Self.Generator.Element] {
var array : [Self.Generator.Element] = []
for element in self {
do {
if try includeElement (element) {
array.append(element)
}
}
}
return array
}
}
The only change is the array declaration (var array : [Self.Generator.Element] = []).
Swift's type inference is getting confused about what you mean. This could mean two things:
var array = [Self.Generator.Element]()
You might mean (and do mean) that you want to call the constructor for the type Array<Self.Generator.Element>.
But you might mean (and the compiler thinks you mean) that you want to create an array of Self.Generator.Element.Type and then you want to call it as a function.
I don't remember this being a compiler confusion in the past, and it may be a regression. That said, it is ambiguous (though only one really makes sense in the end, and I would think you'd need to have said Generator.Element.self there to refer to the type, so maybe it's a real compiler bug). You can remove the ambiguity by using the preferred way to initialize arrays, by using their type:
var array: [Self.Generator.Element] = []

Array<T> extension to filter and unwrap optionals

I'm trying to filter out an array of optionals and unwrap them.
So I wrote the following extension to Array struct but I'm getting the error: 'T' is not a subtype of 'UInt8'
Any idea if this is possible with swift ?
extension Array {
func filterOptionals() -> [T] {
return filter({$0 != nil }).map({$0!})
}
}
The array can contain elements of any type, which is not necessarily 'Optional<T>'. It is not currently possible, and might never be, to write specialised extensions of generic types. We must use global functions instead:
func filterOptionals<T>(array: [T?]) -> [T] {
return array.filter {$0 != nil } .map {$0!}
}
Yes, it is possible. Here is how:
extension Array {
func catOptionals<A>() -> [A] where Element == A? {
return self.flatMap{ $0 }
}
}

Resources