Swift3: type inference inside generic extension - arrays

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!)

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.

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

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
}
}
}

Check if my array contains a dictionary

I have an array that contains a mixture of strings, arrays, and dictionaries. I would like to check if it contains a dictionary. I am trying the following, but receiving errors:
if array.contains(Dictionary<String, AnyObject>) {
}
How would I accomplish this?
You don't have to bother with overloading the contains method if you don't want to – there's already a version of it that can take a custom predicate to check against each element to determine whether it should be considered to be 'in the array'.
It has the advantage of returning early as soon as it finds a match, unlike filter. You could still turn this into an extension method if you really want – but in my opinion it's so concise you shouldn't have to.
if array.contains({$0 is [String:AnyObject]}) {
print("contains dictionary")
} else {
print("doesn't contain dictionary")
}
#Ryan, the type does not have to conform to the Equatable protocol.
This would get the job done:
extension Array {
// This is a concise, yet inefficient implementation
func contains<T>(type type : T.Type) -> Bool {
return !filter({ $0 is T }).isEmpty
}
// Here is a more efficient implementation
func contains<T>(type type : T.Type) -> Bool {
var contains = false
for element in self {
if element is T {
contains = true
break
}
}
return contains
}
}
It can be used as such:
if array.contains(type: Dictionary<String, AnyObject>.self) {
// Run code if the array holds a dictionary of the given type
}
This requires that the array elements conform to the Equatable protocol (which Dictionary doesn't).
You would have to extend contains with something like this:
extension Array {
func contains<T where T : Equatable>(obj: T) -> Bool {
return self.filter({$0 as? T == obj}).count > 0
}
}

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] = []

Cast value to array of unknown `Element` type

Assuming a function that operates on any Array:
func g<T>(array: [T]) -> Void {
// ...
}
...and a function that receives a value of type Any:
func f(x: Any) -> Void {
if x is Array {
// g(?)
}
}
How can I get a properly typed version of x that I can pass to g?
Note that g doesn't rely (explicitly) on the type T. All it needs is the ability to iterate over the elements, which can be treated as values of type Any. Any solution that rewrites g to remove the type parameter is therefore also acceptable.
Edit: to make things harder, I'd like this to work on Linux, where Swift arrays aren't NSArrays until you've called .bridge() on them.
Warning Please note that the below results are based on my experiments on Mirror and work in Swift 2.1.1 (Xcode 7.1.1). It might be that future Swift versions will change the Mirror behaviour.
If finding out dynamically if the value is an array of any type, you could use Swift's reflection support, via the Mirror struct:
func processArray(array: Any) -> Bool {
let mirror = Mirror(reflecting: array)
guard mirror.displayStyle == .Collection else {
return false
}
print("array has \(mirror.children.count) elements")
return true
}
func f(x: Any) {
if processArray(x) {
// i just found an array
}
}
f([1, 2, 3, "4"]) // prints "array has 4 elements"
if you want to keep the generic, you could do this instead:
func g<T>(array: [T]) -> Void {
}
func f(x: Any) -> Void {
if let array = x as? [Any] {
g(array)
}
}

Resources