Swift Array.filter method - arrays

Swift's filter method is defined as follows:
func filter(includeElement: (T) -> Bool) -> [T]
Why does filter definition in Swift's array does not have <T> in its definition (i.e. filter(...))?

filter is a method of the Array<T> class, so T is specified at class level and there is no need to replicate that in the method - actually doing that at method level is a mistake:
struct Array<T> ... {
func filter<V>(includeElement: (V) -> Bool) -> [V]
}
because V is a different type that has no relationship with T (unless you set constraints in the generic definition). By mistake I mean T and V are different types, whereas it could be thought they are the same. Having a generic class with a generic method is perfectly legit though.

it is because this function is defined in an extension to Array struct and the definition of Array has the in its definition : struct Array
class AA<T> {
}
extension AA {
func something(aThing: T) -> T{
return aThing
}
}

Related

Unable to append string to mutable array in Swift

Context - I'm currently learning Swift Struct. So I decided to create my own Phone structure in Playground. (see below)
Problem -
The downloadApp method on the phone struct is throwing the following error.. Cannot use mutating member on immutable value: 'self' is immutable.
Expect outcome - To peacefully append new strings to my apps property, which is an array of strings.
Swift code
struct Phone {
let capacity : Int
let Brand : Sting
var name: String
let model: String
var apps: [String]
var contacts: [String]
var formatCapacity: String {
return "\(capacity)GB"
}
func downloadApp(name: String){
apps.append(name) // ERROR
}
}
You simply need to mark downloadApp as mutating. The issue is caused by the fact that downloadApp is declared in a struct type, which is a value type and hence if you mutate any properties (namely, the apps array here) of the struct, you actually mutate the struct itself. By marking the function as mutating, the compiler allows such modification of the struct through a member function of the type.
mutating func downloadApp(name: String){
apps.append(name)
}

Swift 3 - Binary operator '==' cannot be applied to two 'MyStruct' operands [duplicate]

This question already has answers here:
How to resolve "Binary operator '==' cannot be applied to two 'Foo' operands"?
(2 answers)
Closed 6 years ago.
I have a custom struct...
struct MyStruct {
let viewController: UIViewController
let view:UIView
init(viewController:UIViewController) {
self.viewController = viewController
}
}
I have an array of them, and then I want to check if this array contains a specific struct...
let aStruct = someStruct
if structArray.filter { $0 == aStruct } {
print("do stuff")
}
but I'm getting the error Binary operator '==' cannot be applied to two 'MyStruct' operands ... first, why can't I filter structs in this way... second how should I search the array if not this way...
Thanks in advance
You need the struct to conform to the Equatable protocol to allow it to determine what is considered 'equal'.
Assuming you consider them equal if they have the same viewController property, you could do something like this:
extension MyStruct: Equatable{}
func ==(lhs: MyStruct, rhs: MyStruct) -> Bool {
return lhs.viewController == rhs.viewController;
}

Swift Array of Self-defined Objects Contains: Requires to Insert 'where:'

Hi I'm new to Swift and I'm trying to implement a Graph structure. I have defined Node and Edge structs, and now I'm trying to check whether a particular edge exists in the graph or not. The class and related function is like this:
class Graph<T: Equatable> {
private var storedNodes = [Node]()
private var storedEdges = [Edge]()
func containsSimpleGraphEdge(targetEdge: Edge) -> Bool {
let simpleGraphEdges = self.storedEdges.map({edge in (edge.source, edge.dest)})
let targetSimpleGraphEdge = (targetEdge.source, targetEdge.dest)
return simpleGraphEdges.contains(targetSimpleGraphEdge)
}
But Xcode is complaining that I should insert 'where: ' in the last contains statement. May I know why it's like this? My purpose is just to check whether an edge exists in the graph or not. The mapping to tuples is done because in the Edge implementation there are other properties; here only the source node and the destination node need to be checked.
But Xcode is complaining that I should insert where: in the last
contains statement. May I know why it's like this?
As pointed out by #Keiwan below, tuples don't conform to Equatable, even if Swift's standard lib provides us with equality testing operator == for tuples up to 6 members, given that all members themselves conform to Equatable; the latter as described in the following Swift Evolution proposal:
Se-0015: Tuple comparison operators
Hence, your attempted call to contains(_:) fails as this method is not accessible for non-Equatable elements.
You could, however, make use of contains(where:) to explicitly provide a closure telling contains how to test equality. Given that your Node type conforms to Equatable, you'll want to modify the contains call in the return of your method into:
return simpleGraphEdges.contains(where: { $0 == targetSimpleGraphEdge })
// note also your typo here, 'targetSimpleEdge' should be 'targetSimpleGraphEdge'
Or, supplying the predicate as a trailing closure:
return simpleGraphEdges.contains { $0 == targetSimpleGraphEdge }
If you don't want to make Edge equatable (such as when it has multiple context-dependant definitions of "equality"), you can just use contains(where:).
func contains(simpleGraphEdge e: Edge) -> Bool {
return self.storedEdges.contains(where: {
$0.source == e.source && $0.dest == e.dest
})
}

Append to generic array in Swift

I have a protocol named Foo and a struct named Bar. Bar conforms to Foo.
protocol Foo {}
struct Bar: Foo {}
Appending a Bar instance to an array of Bar works as expected.
var array = [Bar]()
array.append(Bar())
Now, I have a generic struct called Baz that's initialized with a type that conforms to Foo (e.g. Bar).
struct Baz<T: Foo> {
private(set) var array = [T]()
init() {
if T.self is Bar.Type {
// Error: Cannot invoke 'append' with an argument list of type (Bar)
array.append(Bar())
}
}
}
Attempting to append to the array results in the following error:
Cannot invoke 'append' with an argument list of type (Bar)
Why doesn't this work as expected?
As an aside, the use case is something like this:
let bazBarStack = Baz<Bar>().array
let bazQuxStack = Baz<Qux>().array
You need to store objects in array, that are of type T. Therefore you should cast using as! T:
struct Baz<T: Foo> {
private(set) var array = [T]()
init() {
if T.self is Bar.Type {
array.append(Bar() as! T)
}
}
}
Because array holds elements of a single type T, which must conform to Foo but is not necessarily compatible with Bar. It looks like you wish to have an array that can hold anything conforming to Foo; in this case, get rid of the generics altogether and simply write
private(set) var array = [Foo]()
and then your init will be
init() {
array.append(Bar())
}

SWIFT how to store a CLASS containing a STRUCT with an ARRAY of STRUCT inside USERDEFAULTS

the problem may sound weird but is exactly my situation.
I have a class containing a struct with only an array inside: an array of another struct. I would like to store an object of the class into user default. Let see some code
struct Inside {
var something: String
var somethingElse: Int
}
struct Outside {
var array = [Inside]()
}
class TheClass: {
var array = [Outside]()
}
var a = TheClass()
//now I would like to put 'a' into UserDefaults
I tried to use NSCoding, like you can see in this code (the code compiles)
class TheClass: NSObject, NSCoding {
var array: NSMutableArray = NSMutableArray()
override init(){
self.elementi = []
}
required init(coder aDecoder: NSCoder) {
elementi = aDecoder.decodeObjectForKey("elementi") as! NSMutableArray
}
func encodeWithCoder(aCoder: NSCoder) {
aCoder.encodeObject(elementi, forKey: "elementi")
}
}
but when I try to add an object of struct Outside into the NSMutableArray, I get an error. I found out that one solution is to convert into an NSValue. Here is the code in objective C, I am not able to translate it in swift
//here 'a' denotes an object of Outside struct that we want to insert into the NSMutableArray stored inside an object of TheClass (we cannot add it directly to the NSMutableArray)
NSValue* value = [NSValue value:&a withObjCType:#encode(Outside)];
//To extract it later:
[value getValue:&value];
I don't know how to solve this problem. Maybe I can change the struct to classes, in this way NSObject is no more need and I can use NSCoding directly (can be used with class object but not with struct object). But if I do this way I will get exactly the error that this guy have: SWIFT: Copying objects from arrays then changing properties and adding them back to original array
What do you suggest?

Resources