swift dictionary nested array manipulation - cannot mutate nested array inside dictionary - dictionary

var dict = ["alpha": ["a", "b", "c", "d"]]
// output : ["alpha": ["a", "b", "c", "d"]]
var alphaList = dict["alpha"]
// output : {["a", "b", "c", "d"]
alphaList?.removeAtIndex(1)
// output : {Some "b"}
alphaList
// output : {["a", "c", "d"]}
dict
// output : ["alpha": ["a", "b", "c", "d"]]
Why is 'dict' not altered? Is it because 'alphaList' is a copy of the array and not the actual array inside the dictionary? Can anyone point me where in Swift language documentation I can find this information?
What is the correct/functional way to manipulate values (of complex type) of a dictionary?

Good question yes it creates the copy of the value in your case the value is Array
var alphaList = dict["alpha"]
/* which is the copy of original array
changing it will change the local array alphaList as you can see by your output */
output : {Some "b"}
In order to get the original array directly use
dict["alpha"]?.removeAtIndex(1)
Or update it using the key
alphaList?.removeAtIndex(1)
dict["alpha"] = alphaList
Apple : Assignment and Copy Behavior for Strings, Arrays, and Dictionaries
Swift’s String, Array, and Dictionary types are implemented as structures. This means that strings, arrays, and dictionaries are copied when they are assigned to a new constant or variable, or when they are passed to a function or method.
This behavior is different from NSString, NSArray, and NSDictionary in Foundation, which are implemented as classes, not structures. NSString, NSArray, and NSDictionary instances are always assigned and passed around as a reference to an existing instance, rather than as a copy. "

Swift arrays and dictionaries are value (struct) times. There are only one reference to such instance. While NSArray and NSDictonary are class type and there could be multiple references to such instances.
The statement var alphaList = dict["alpha"] makes copy for ["a", "b", "c", "d"], so you cannot change the original array.
If you want to mutate original "alpha" you have to use dict as root variable:
dict["alpha"]?.removeAtIndex(1)

You are right, alphaList is a copy, hence any change you make on it is local and does not affect the original array. The documentation describes that in Structures and Enumerations Are Value Types.
The only way to make changes to an array after extracting it from somewhere else is by passing the array to a function by reference, using the inout parameter modifier:
func modifyAlphaList(inout alphaList: [String]?) {
alphaList?.removeAtIndex(1)
}
modifyAlphaList(&dict["alpha"])
dict // (.0 "alpha", ["a", "c", "d"])

This link should help
https://developer.apple.com/library/prerelease/mac/documentation/Swift/Conceptual/Swift_Programming_Language/ClassesAndStructures.html#//apple_ref/doc/uid/TP40014097-CH13-XID_150
Swift’s String, Array, and Dictionary types are implemented as structures. This means that strings, arrays, and dictionaries are copied when they are assigned to a new constant or variable, or when they are passed to a function or method.

Related

How to access a Python dictionary name with string?

I have string "dict_1", and I have dictionary named dict_1.
x = "dict_1"
dict_1 = {"a": "b", "c": "d"}
So, can I access dict_1 like x['a']?
I found a Stack Overflow Question, but in this question, the OP wants to create variable with string, not access variable (dictionary) with string.
I really need some help. I'm using Python 3.8 and Windows.
Thanks!
The way I see it, you have two basic options here.
The most obvious way is to use eval to convert the value of the string x into a usable Python object - in this case the dictionary dict_1.
Note that eval executes a string as Python code. If the string is coming from an untrusted source, then this is quite dangerous to do. You should use this with caution, and apply appropriate sanitization to any inputs.
x = "dict_1"
dict_1 = {"a": "b", "c": "d"}
print(type(eval(x)))
# Prints <class 'dict'>
print(eval(x)['a'])
# Prints "b"
The other method is to make use of locals(). This returns a dictionary of variables defined in the local scope. You would index it with x, which would give you the dictionary itself as with eval.
x = "dict_1"
dict_1 = {"a": "b", "c": "d"}
print(type(locals()[x]))
# Prints <class 'dict'>
print(locals()[x]['a'])
# Prints "b"
In theory, using locals() for the job would prove to be safer since with eval, you may inadvertently execute arbitrary code.
Once again, if coming from an untrusted source, you should do some basic sanity checks, e.g. check that the variable is in scope and is a dictionary:
if x in locals() and type(locals()[x]) == dict:
print(locals()[x]['a'])
# Prints "b"

Select property from each object in a list

Say I have a List<Tuple>, where the first element in each is a string. Is there an extension function in Kotlin to select the first element from each of these tuples?
I'm looking for something like the C# LINQ syntax for Select:
myTuples.Select(t => t.item1)
In Kotlin, a Tuple could be a Pair or a Triple. You could just map over the list and select out the first element, like this:
val myTuples : List<Triple<String,String,String>> = listOf(
Triple("A", "B", "C"),
Triple("D", "E", "F")
)
val myFirstElements: List<String> = myTuples.map { it.first } // ["A", "D"]
And of course, you can leave off the types, I've left them in to make this easier to follow.

How to access dictionary key via index?

Is there a way to access dictionary keys in Swift 2 using an index?
var dict = ["item1":1, "item2":2, "item3":3]
dict.keys[0]
results in the error:
15:29: note: overloads for 'subscript' exist with these partially
matching parameter lists: (Base.Index), (Range),
(Self.Index)
print("key=" + dict.keys[i])
I saw an examples from August (Swift: dictionary access via index) doing this:
dict.keys.array[0]
At least in Swift 2, there isn't an array object on dictionary keys.
In Swift 2 the equivalent of dict.keys.array would be Array(dict.keys):
let dict = ["item1":1, "item2":2, "item3":3]
let firstKey = Array(dict.keys)[0] // "item3"
Note: of course, as dictionaries are unordered collections, "first key" of the resulting array may not have a predictable value.
do not rely on order of items in a dictonary, using array directly would be better in your case, you can also handle key/value in array with making array units objects.

Go array for method

Can I use array and its pointer for Go methods?
I have the following code:
var array = [3]string{"A", "B", "C"}
type arrayTypePt *[3]string
func (m *arrayTypePt) change() { m[1] = "W" }
func main() {
(arrayTypePt(&array)).changeArray4()
}
But this code: http://play.golang.org/p/mXDEhmA9wk
give me an error of:
invalid receiver type *arrayTypePt (arrayTypePt is a pointer type)
invalid operation: m[1] (type *arrayTypePt does not support indexing)
arrayTypePt(&array).changeArray4 undefined (type arrayTypePt has no field or method changeArray4)
I get the same error when I try this with slice.
Why I cannot do this in method?
The receiver type of a method cannot be a pointer to a pointer, but that is what you wrote:
func (m *arrayTypePt) change() { m[1] = "W" }
arrayTypePt is already a pointer *[3]string. Quoting from the language specification:
[The receiver] type must be of the form T or *T (possibly using parentheses) where T is a type name. The type denoted by T is called the receiver base type; it must not be a pointer or interface type and it must be declared in the same package as the method.
Your 2nd error ("type *arrayTypePt does not support indexing") is also a result of this (m is a pointer to pointer, that's why you can't index it; if it would only be a pointer to array or slice, the pointer indirection would be automatic).
Your 3rd error is simply a typo, you declared a method named change() and not changeArray4().
So you should only name the non-pointer array type:
type arrayType [3]string
And you can declare your array using directly this type:
var array = arrayType{"A", "B", "C"}
And you can simply call its change() method:
array.change()
The address of the array will be taken automatically (because the change() method has a pointer receiver but the array variable itself is not a pointer).
Try it on the Go Playground.
Notes / Alternatives
If you would want your array variable to be explicitly [3]string, you could still make it work by converting it to arrayType, setting it to another variable, and change() can be called on this (because being a variable its address can be taken - while the address of a conversion like arrayType(arr) cannot):
arr2 := [3]string{"A", "B", "C"}
arr3 := arrayType(arr2)
arr3.change()
Or if you would declare your variable to be a pointer to type [3]string, you could save the required additional variable (which was only required so we were able to take its address):
arr4 := new([3]string)
*arr4 = [3]string{"A", "B", "C"}
((*arrayType)(arr4)).change()
Try these variants too on the Go Playground.
It works fine if you define the array/slice variable as the receiver type:
type arrayType [3]string
var array arrayType = [3]string{"A", "B", "C"} # note "arrayType" here
func (m *arrayType) change() { m[1] = "W" }
func main() {
array.change()
}
I'm not sure why the typecasting behaves like it does.

Swift dictionary bug?

So I started a project in Swift, and I've come to this problem:
this code works:
var dictionary = ["a":"valueOfA","b":"valueOfB","c":"valueOfC"]
println(dictionary)
dictionary["c"] = "newValOfC"
println(dictionary)
and this doesn't:
var dictionary = [:]
dictionary = ["a":"valueOfA","b":"valueOfB","c":"valueOfC"]
println(dictionary)
dictionary["c"] = "newValOfC"
println(dictionary)
Gives an error:
Playground execution failed: error: <REPL>:35:17: error: cannot assign to the result of this expression
dictionary["c"] = "newValC"
~~~~~~~~~~~~~~~ ^
Notice that this is not a constant value
So why doesn't the line
dictionary = ["a":"valueOfA","b":"valueOfB","c":"valueOfC"]
give an error?
Since the context does not provide enough information to infer the type, you'll need to explicitly name it as a dictionary, otherwise swift assumes it is an NSDictionary (I'm not clear on why though. I assume for better obj-c compatibility):
The following code all works:
// Playground
import UIKit
var str:NSString = "Hello, playground"
var d0 = [:]
var d1: Dictionary = [:]
d0.setValue(UIWebView(), forKey: "asdf")
d1["asdf"] = 1
d1["qwer"] = "qwer"
Okay, I found it, the problem is that by initializing an empty dictionary, the type inference gets a little crazy.
You'll need this code:
var dictionary = Dictionary<String, String>()
instead of
var dictionary = [:]
but that still does not explain why the line
dictionary = ["a":"valueOfA","b":"valueOfB","c":"valueOfC"]
does not give an error
And referring to Swift Language Guide, the
dictionary = [:]
syntax is correct "if the context already provides type information".
The big difference is that
var dictionary = [:]
doesn't give any chance the compiler to infer the right type, whereas
var dictionary = ["a":"valueOfA","b":"valueOfB","c":"valueOfC"]
does.
Actually it looks like the first line produces a __NSDictionaryI instance, whereas the second one produces a Dictionary<String,String as expected.
From The Swift Programming Language book
If the context already provides type information, create an empty dictionary with an empty dictionary literal, which is written as [:] (a colon inside a pair of square brackets):
which means the first line is ambiguous.
The [:] syntax only works if the current context has enough information to be able to infer what the types are. Here are a few ways where the context is known...
As has been mentioned already, the code below works because it can be inferred that the keys and values are strings:
var dictionary = [ "a" : "valueOfA" , "b" : "valueOfB" , "c" : "valueOfC" ]
A dictionary passed into a function has a known context:
func dictionaryFunc(var dictionary : Dictionary<String, String>) {
// Do stuff with the dictionary
}
In both cases, the key/value types for the dictionary are now known, so you could do this to create a new dictionary with the same variable name...
dictionary = [:]
In the first case, if you used the [:] syntax after the declaration you would be erasing the contents of the original dictionary. In the second case, dictionaries get copied when they get passed into functions, so using [:] would only be erasing the copy.
In Swift 2.0 I'm using this:
var dictionary = [String:String]()
dictionary["key1"] = "value1"

Resources