Change parameter of an object if list has a specific key [duplicate] - swift

I am still not sure about the rules of struct copy or reference.
I want to mutate a struct object while iterating on it from an array:
For instance in this case I would like to change the background color
but the compiler is yelling at me
struct Options {
var backgroundColor = UIColor.blackColor()
}
var arrayOfMyStruct = [MyStruct]
...
for obj in arrayOfMyStruct {
obj.backgroundColor = UIColor.redColor() // ! get an error
}

struct are value types, thus in the for loop you are dealing with a copy.
Just as a test you might try this:
Swift 3:
struct Options {
var backgroundColor = UIColor.black
}
var arrayOfMyStruct = [Options]()
for (index, _) in arrayOfMyStruct.enumerated() {
arrayOfMyStruct[index].backgroundColor = UIColor.red
}
Swift 2:
struct Options {
var backgroundColor = UIColor.blackColor()
}
var arrayOfMyStruct = [Options]()
for (index, _) in enumerate(arrayOfMyStruct) {
arrayOfMyStruct[index].backgroundColor = UIColor.redColor()
}
Here you just enumerate the index, and access directly the value stored in the array.
Hope this helps.

You can use use Array.indices:
for index in arrayOfMyStruct.indices {
arrayOfMyStruct[index].backgroundColor = UIColor.red
}

You are working with struct objects which are copied to local variable when using for in loop. Also array is a struct object, so if you want to mutate all members of the array, you have to create modified copy of original array filled by modified copies of original objects.
arrayOfMyStruct = arrayOfMyStruct.map { obj in
var obj = obj
obj.backgroundColor = .red
return obj
}
It can be simplified by adding this Array extension.
Swift 4
extension Array {
mutating func mutateEach(by transform: (inout Element) throws -> Void) rethrows {
self = try map { el in
var el = el
try transform(&el)
return el
}
}
}
Usage
arrayOfMyStruct.mutateEach { obj in
obj.backgroundColor = .red
}

For Swift 3, use the enumerated() method.
For example:
for (index, _) in arrayOfMyStruct.enumerated() {
arrayOfMyStruct[index].backgroundColor = UIColor.redColor()
}
The tuple also includes a copy of the object, so you could use for (index, object) instead to get to the object directly, but since it's a copy you would not be able to mutate the array in this way, and should use the index to do so. To directly quote the documentation:
If you need the integer index of each item as well as its value, use
the enumerated() method to iterate over the array instead. For each
item in the array, the enumerated() method returns a tuple composed of
an integer and the item.

Another way not to write subscript expression every time.
struct Options {
var backgroundColor = UIColor.black
}
var arrayOfMyStruct = [Options(), Options(), Options()]
for index in arrayOfMyStruct.indices {
var option: Options {
get { arrayOfMyStruct[index] }
set { arrayOfMyStruct[index] = newValue }
}
option.backgroundColor = .red
}

I saw this method in some code and it seems to be working
for (var mutableStruct) in arrayOfMyStruct {
mutableStruct.backgroundColor = UIColor.redColor()
}

Related

How to put a variable in a for loop with different end number

I’ve got multiple variables with the same name but a different number on the end like this:
MyVar1, MyVar2, MyVar3, MyVar4
and they are either true or false
How do I write this correctly?
For index in Range(1...4){
if (“MyVar” + index) == true{
print(“:)”)
}
}
didnt tries the solution but according to this post: https://stackoverflow.com/a/45622489/5464805 you can do this:
Edit: I read that your variables are Booleans
for i in 0...4 {
if let var = myObject.value(forKey: "MyVar\(i)") as Bool,
var { // If var is successfully cast and true it enters the statement
print(var)
}
}
HOWEVER
All the other solutions above or below are still correct. This is something you dont usually do in Swift in general unless you must do so.
You should either rewrite your model and give a proper name to each variables, that you will later put into an array such as...
class MyObject {
var MyVar1: Bool // To Rename
var MyVar2: Bool // To Rename
var MyVar3: Bool // To Rename
var MyVars: [Bool] {
get { return [MyVar1, MyVar2, MyVar3] }
}
}
Or you completely get rid of these variable and create an Array directly
class MyObject {
// var MyVar1: Bool // To Delete
// var MyVar2: Bool // To Delete
// var MyVar3: Bool // To Delete
var MyVars: [Bool]
}
and you set the Array / Dictionnary in accordance to your needs
You are trying to get a sum of String ("MyVar") and Int ("index") and compare that sum with Bool (true)
It would be more safety to store all your variables in an array like that
let myVars = [MyVar1, MyVar2, MyVar3, MyVar4]
Then you can iterate throught the array:
for i in myVars {
if i {
print(i)
}
}
You can't create a variable from concatenation at runtime , you need to have an array of bool like this
var arr = [false,true,true,false] // var1 , var2 , var3 , var4
for item in arr {
if item {
}
}

Swift 3: Change item in dictionary

I'm saving lists in a dictionary. These lists need to be updated. But when searching for an item, I need [] operator. When I save the result to a variable, a copy is used. This can not be used, to change the list itself:
item = dicMyList[key]
if item != nil {
// add it to existing list
dicMyList[key]!.list.append(filename)
// item?.list.append(filename)
}
I know, that I need the uncommented code above, but this accesses and searches again in dictionary. How can I save the result, without searching again? (like the commented line)
I want to speed up the code.
In case you needn't verify whether the inner list was actually existing or not prior to adding element fileName, you could use a more compact solution making use of the nil coalescing operator.
// example setup
var dicMyList = [1: ["foo.sig", "bar.cc"]] // [Int: [String]] dict
var key = 1
var fileName = "baz.h"
// "append" (copy-in/copy-out) 'fileName' to inner array associated
// with 'key'; constructing a new key-value pair in case none exist
dicMyList[key] = (dicMyList[key] ?? []) + [fileName]
print(dicMyList) // [1: ["foo.sig", "bar.cc", "baz.h"]]
// same method used for non-existant key
key = 2
fileName = "bax.swift"
dicMyList[key] = (dicMyList[key] ?? []) + [fileName]
print(dicMyList) // [2: ["bax.swift"], 1: ["foo.sig", "bar.cc", "baz.h"]]
Dictionaries and arrays are value types. So if you change an entry you'll need to save it back into the dictionary.
if var list = dicMyList[key] {
list.append(filename)
dicMyList[key] = list
} else {
dicMyList[key] = [filename]
}
It's a little bit late, but you can do something like this:
extension Optional where Wrapped == Array<String> {
mutating func append(_ element: String) {
if self == nil {
self = [element]
}
else {
self!.append(element)
}
}
}
var dictionary = [String: [String]]()
dictionary["Hola"].append("Chau")
You can try this in the Playground and then adapt to your needs.

Calling instance method on each object in array

Let's assume this situation: I have an array of objects and I want call instance method on each one of them. I can do something like that:
//items is an array of objects with instanceMethod() available
items.forEach { $0.instanceMethod() }
The same situation is with map. For example I want to map each object to something else with mappingInstanceMethod which returns value:
let mappedItems = items.map { $0.mappingInstanceMethod() }
Is there a cleaner way to do that?
For example in Java one can do:
items.forEach(Item::instanceMethod);
instead of
items.forEach((item) -> { item.instanceMethod(); });
Is similiar syntax available in Swift?
What you are doing in
items.forEach { $0.instanceMethod() }
let mappedItems = items.map { $0.mappingInstanceMethod() }
is a clean and Swifty way. As explained in Is there a way to reference instance function when calling SequenceType.forEach?, the first statement cannot be reduced
to
items.forEach(Item.instanceMethod)
There is one exception though: It works with init methods
which take a single argument. Example:
let ints = [1, 2, 3]
let strings = ints.map(String.init)
print(strings) // ["1", "2", "3"]
for item in items {
item.instanceMethod()
}
have you tried
let mappedItems = items.map { $0.mappingInstanceMethod() }
note the () to call the method
Edit 1:
sample code:
class SomeObject {
func someFunction() -> Int {
return 5
}
}
let array = [SomeObject(), SomeObject(), SomeObject()]
let ints = array.map { $0.someFunction()}
print(ints)

How to remove duplicate data in the UIPickerView in Swift

May I know how to remove the duplicate data which all the data is passing from the Parse database. I just select one column and pass that column data to this picker view.
I've try to find the query.wherekey but it didn't have the DISTINCT function which similar to the SQL statement, so what should I do to avoid the data duplication?
As you add items one by one into your self.pickerString array, just verify if this array contains or not the new value before addObject new value to the array. For example:
if !self.pickerString.contains(object["Intake"] as! String) {
self.pickerString.append(object["Intake"] as! String)
}
There is no built-in function to remove duplicate items in an array in swift (correct me if I'm wrong). However, you can add new features for array with 'extension' keyword:
extension Array {
func distinct<T: Equatable>() -> [T] {
var newArray = [T]()
for item in self {
if !contains(newArray, item as! T) {
newArray.append(item as! T)
}
}
return newArray
}
}
usage:
let s = ["344","333","1","2","333"]
var p = s.distinct() as [String]
I don't think you have anything like distinct on Parse, you'll have to download all rows and then filter out equal values.
For a functional approach:
func distinct<T: Equatable>(source: [T]) -> [T] {
var unique = [T]()
for item in source {
if !contains(unique, item) {
unique.append(item)
}
}
return unique
}
[...]
self.pickerString = distinct(objects)
[...]

Nested Swift Dictionaries

I want to initialize a dictionary with a dictionary nested inside like this:
var a = [Int:[Int:Float]]()
a[1][2] = 12
But I get an error:
(Int:[Int:Float]) does not have a member named 'subscript'
I've hacked at a variety of other approaches, all of them running into some kind of issue.
Any idea why this doesn't work?
You can create your own 2D dictionary like this:
struct Dict2D<X:Hashable,Y:Hashable,V> {
var values = [X:[Y:V]]()
subscript (x:X, y:Y)->V? {
get { return values[x]?[y] }
set {
if values[x] == nil {
values[x] = [Y:V]()
}
values[x]![y] = newValue
}
}
}
var a = Dict2D<Int,Int,Float>()
a[1,2] = 12
println(a[1,2]) // Optional(12.0)
println(a[0,2]) // nil
The point is you access the element via a[x,y] instead of a[x][y] or a[x]?[y].
It's giving you that error because your first subscript returns an optional so it may return a dictionary or nil. In the case that it returns nil the second subscript would be invalid. You can force it to unwrap the optional value by using an exlamation point.
var a = [1 : [ 2: 3.14]]
a[1]
a[1]![2]
If you aren't positive that a[1] is non-nil you may want to safely unwrap with a question mark instead.
var a = [1 : [ 2: 3.14]]
a[1]
a[1]?[2]
You can also assign using this method. (As of Beta 5)
var a = [Int:[Int:Float]]()
a[1] = [Int: Float]()
a[1]?[2] = 12.0
a[1]?[2] //12.0
Another way to do it is with an extension to the standard dictionary:
extension Dictionary {
mutating func updateValueForKey(key: Key, updater: ((previousValue: Value?) -> Value)) {
let previousValue = self[key]
self[key] = updater(previousValue: previousValue)
}
}
Example:
var a = [Int:[Int:Float]]()
a.updateValueForKey(1) { nestedDict in
var nestedDict = nestedDict ?? [Int:Float]()
nestedDict[2] = 12
return nestedDict
}