I would like to create a Swift 4 extension on a Swift Array. The function should sort the array in-place.
The compiler complains seems to assume that arrays are immutable, as it complains on the function I created. I'd like to solve this, but do not know how. Please do note that the requirement is to sort the array in-place (with sort) and not creating a new array (like one would do with sorted).
struct MyStruct {
var field: String
}
typealias MyStructList = [MyStruct]
// requirement: extend MyStructList with custom sort
extension Array where Element == MyStruct {
func customSort() -> [MyStruct] {
return sort { $0.field < $1.field }
}
}
Compiler complains: Cannot use mutating member on immutable value: 'self' is immutable
You want to be calling sorted(by:), which returns a new, sorted Array instance rather than sort(by:), which does sorting in place and hence mutates the Array.
extension Array where Element == MyStruct {
func customSort() -> [MyStruct] {
return sorted(by: {$0.field < $1.field})
}
}
If you actually want to sort the Array in place, you have to mark your customSort function as mutating and change the function signature to return Void, since there's no new Array created.
extension Array where Element == MyStruct {
mutating func customSort() {
sort(by: {$0.field < $1.field})
}
}
Two issues:
You have to mark the function as mutating
If you want to sort in place there is no return value
extension Array where Element == MyStruct {
mutating func customSort() {
sort { $0.field < $1.field }
}
}
And please name methods / functions camelCased.
sort is a mutable function that sorts an Array in-place. sorted(by:) is the function you are looking for. Rename sort to sorted.
If you are looking to sort the Array in-place, then rewrite your function declaration to include the mutating qualifier.
So the following:
func custom_sort()
becomes:
mutating func custom_sort()
The mutating function sort(by:) does not return anything, therefore your return is erroneous. Remove -> [MyStruct] as well.
Related
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.
I have an array, within an array situation, and I want to constrain the internal Array's element type.
// this doesn't work but should illustrate what I want. Is there
// any way to constrain the Element type for an Array when it's
// already declared as the type for an Array
extension Array where Element == Array<Element2: SomeProtocol> {
}
Ultimately, I think that solving this problem will fix the following compile error
protocol Thingy {
associatedtype MeasurementType: Measurement
var topMeasurements: [[MeasurementType]] { get }
}
extension Thingy {
func doStuff() {
// COMPILE ERROR'[[Self.MeasurementType]]' is not convertible to 'Array<Array<Measurement>>'
let compileError = self.topMeasurements.measurements(forUUIDs: ["A"])
// Hack workaround
// The fact that this works leads me to believe that the constraint in the
// extension is inadequate. The extension wants [[Measurement]] and
// event though MeasurementType is a kind of Measurement the compiler won't
// allow it. The hope is that if we could write something like
// [[Element]] where Element: Measurement
let measurements: [[Measurement]] = self.topMeasurements
let thisWorks = measurements.doSomething(ids: ["A"])
print(thisWorks)
}
}
// I'm hoping that if I constrain the following that it'll fix my issue
// constrain this type ----------------------
// |
// \/
extension Array where Element == Array<Measurement> {
func doSomething(ids: [String]) -> [Measurement] {
// do something return some elements,
// I've removed any code that could cause confusion
return []
}
}
This is much easier to implement as an extension on all Collections rather than just on Arrays. I believe the extension you want is this:
extension Collection where Element: Collection, Element.Element: Measurement {
func measurements(forUUIDs: [String]) -> [Measurement] {
return []
}
}
The basic problem is that Swift lacks higher-kinded types, so you can't extend Array (because that's not a proper type). But you can extend Collection (because that's a PAT).
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
}
}
I'm trying to write an extension to Array in Xcode's playground. I want to write a function that will modify the array so that it will be filled with 0s when the function is called. The code I'm attempting to use is this:
import Foundation
extension Array {
mutating func zero() {
for i in 0..<self.count {
self[i] = 0 // ERROR - Ambiguous reference to member 'subscript'
}
}
}
This code does not run, because of the error at:
self[i] = 0
However, if I try to 'get' a value from self, I have no issues. For example:
import Foundation
extension Array {
mutating func zero() {
for i in 0..<self.count {
print(self[i])
}
}
}
has no errors and runs as expected.
So my question is; Why can't I modify the array?
Also, Replacing:
self[i] = 0
with,
self.append(0)
also results in an error. (Cannot invoke 'append' with an argument list of type '(Int)')
So it won't let me modify self at all it seems.
It will work if you do the following:
extension Array where Element: IntegerLiteralConvertible {
mutating func zero() {
for i in 0..<self.count {
self[i] = 0
}
}
}
You must constrain the type of the elements allowed, because you can't, for example, zero out an array of Strings.
It's important to remember, when extending something like an Array, that you take into account what types of elements are valid for the method you're adding. If it requires elements of a certain type, you constrain on it and then it will work. That way you can only use your zero() method on Arrays containing ints. You might define a different version for an array of Strings that replaces everything in the array with the string "zero", for example, and that implementation will only be used on the type that you constrain it to as well.
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] = []