I have a dictionary -> var dictionary = [String : [String]]() and I want to append string values in the array of the dictionary. This is how I'm doing it
for (key, value) in dictionary {
dictionary.updateValue(value.append(nameText),forKey: "name")
}
Here, nameText is a string, I'm getting an error saying,
Cannot use mutating member on immutable value: 'value' is a 'let' constant.
What am I doing wrong? Help would be much appreciated.
Your first issue is that value is a let constant within your loop body. You must declare it as var in order to mutate it.
Your second issue is that you're trying to use value.append(nameText) as the value to set for the key. However, append() mutates the array in place, and returns Void.
Thirdly, don't use updateValue(forKey:). There's really no point. Use subscripting instead.
var dictionary = [
"a" : ["a"],
"b" : ["b", "b"],
"c" : ["c", "c", "c"],
]
let nameText = "foo"
for (key, var value) in dictionary {
value.append(nameText)
dictionary["name"] = value
}
Now, this gets your code to compile, but I'm highly skeptical this is really what you want to do. You'll be overwriting the value for the "name" key on every iteration, meaning only the last iteration's value will persist. Furthermore, because Dictionary doesn't have a defined ordering, this code has indeterminate behaviour. What are you actually trying to do?
Try this:
for (key, value) in dictionary {
dictionary.updateValue(value + [nameText], forKey: key)
}
Think about it for a second; value.append(nameText) is an action. It returns Void (the type for ... nothing!).
You want to update the value to something upon which an action has been performed.
Instead of manually making a temporary copy, modifying that, and then using it to update the value for some key, you can simply use subscripts and extensions:
What you want is:
extension Dictionary
{
public subscript(forceUnwrapping key: Key) -> Value
{
get
{
return self[key]!
}
set
{
self[key] = newValue
}
}
}
So, for a dictionary named dictionary:
for key in dictionary.keys
{
dictionary[forceUnwrapping: key].append(nameText)
}
Specifically, dictionary[forceUnwrapping: key].append(nameText).
/* example setup */
var dictionary: [String: [String]] = ["foo": [], "bar": []]
let nameText = "foobar"
/* append the value of the 'nameText' immutable to each inner array */
dictionary.keys.forEach { dictionary[$0]?.append(nameText) }
/* ok! */
print(dictionary) // ["bar": ["foobar"], "foo": ["foobar"]]
As described in the following Q&A, however
Dictionary in Swift with Mutable Array as value is performing very slow? How to optimize or construct properly?
..., it is good to be aware of the overhead of mutating "in place", especially if working performance tight applications. Taking the advice from the answer in the linked thread above, an alternative, more sensible and less copy-wasteful approach would be e.g.:
var dictionary: [String: [String]] = ["foo": [], "bar": []]
let nameText = "foobar"
dictionary.keys.forEach {
var arr = dictionary.removeValue(forKey: $0) ?? []
arr.append(nameText)
dictionary[$0] = arr
}
print(dictionary) // ["bar": ["foobar"], "foo": ["foobar"]]
Related
The problem with the ECMA standard for sort of Object.keys() is known:
Object.keys() handle all keys with integer (example: 168), including integer as strings (example: "168"), as a integer. The result is, both are the same (168 === "168"), and overwrite itself.
var object = {};
object["168"] = 'x';
object[168] = 'y';
Object.keys(object); // Array [ "168" ]
object[Object.keys(object)]; // "y"
Interestingly, all keys (including pure integer keys) are returned as a string.
The ecma262 wrote about this: All keys will be handle as a integer, expect the key is a String but is not an array index.
https://tc39.es/ecma262/#sec-ordinaryownpropertykeys
That should tell us: 168 === "168". A toString() do not solve the problem.
var object = {};
object[[3].toString()] = 'z';
object[[1].toString()] = 'x';
object[[2].toString()] = 'y';
Object.keys(object);
// Array(3) [ "1", "2", "3" ]
Paradoxically, in this case, only integer apply as "enumerable" (it's ignoring array.sort(), that sort also strings with letters.).
My question about this is simple: How can i prevent the sort function in Object.keys()? I have testet the Object.defineProperties(object, 1, {value: "a", enumerable: true/false}), but that mean not realy enumerable in the case of integer or string or integer-like string. It means only should it be counted with or not. It means "counted" like omit (if it false), not "enumerabled" like ascending or descending.
A answere like that is not a good answer: Please use only letters [a-zA-Z] or leastwise a letter at the first position of keyword.
What I want: That the keys are not sorted, but output in the order in which they were entered, whether integer, string or symbol.
Disclaimer: Please solutions only in JavaScript.
Javascript Objects are unordered by their nature. If you need an ordered object-like variable I would suggest using a map.
To achieve what you're looking for with a map instead of object you'd do something like the below:
var map1 = new Map();
map1.set("123", "c");
map1.set(123, "b");
var iterator1 = map1.keys();
var myarray = [];
for (var i = 0; i < map1.size; i++) {
myarray.push(iterator1.next().value);
}
console.log(myarray);
// Array ["123", 123]
Unfortunately it's not compatible with IE and I'm not sure how else you could achieve what you need without it. A quick Google did return something about jQuery maps, though.
If you don't want to use jQuery and still need to support IE some points are below:
Is there anything stopping you using an array rather than JS object to store the data you need? This will retain the order per your requirements unlike objects. You could have an object entry in each iteration which represents the key then use a traditional foreach to obtain them as an array. I.e.
The array:
var test_array = [
{key: 123, value: 'a value here'},
{key: "123", value: 'another value here'}
];
// console.log(test_array);
Getting the keys:
var test_array_keys = [];
test_array.forEach(function(obj) { test_array_keys.push(obj['key']); } );
// console.log(test_array_keys);
Then if you needed to check whether the key exists before adding a new entry (to prevent duplicates) you could do:
function key_exists(key, array)
{
return array.indexOf(key) !== -1;
}
if(key_exists('12345', test_array_keys))
{
// won't get here, this is just for example
console.log('Key 12345 exists in array');
}
else if(key_exists('123', test_array_keys))
{
console.log('Key 123 exists in array');
}
Would that work? If not then the only other suggestion would be keeping a separate array alongside the object which tracks the keys and is updated when an entry is added or removed to/from the object.
Object Keys sorted and store in array
First Creating student Object. then sort by key in object,last keys to store in array
const student={tamil:100, english:55, sci:85,soc:57}
const sortobj =Object.fromEntries(Object.entries(student).sort())
console.log(Object.keys(sortobj))
use map instead of an object.
let map = new Map()
map.set("a", 5)
map.set("d", 6)
map.set("b", 12)
to sort the keys (for example, to update a chart data)
let newMap = new Map([...map.entries()].sort())
let keys = Array.from(newMap.keys()) // ['a','b','d']
let values = Array.from(newMap.values()) // [5,12,6]
I want to implement a multiple click in my Shinobi DataGrid. I have a grid which have array
( ["1", "32", and more] )
If I click the grid I put it into new Array self.arrayNr.append(currNr).
But I want to check and remove if currNr is already exist in arrayNr it is will be remove from the arrayNr.
I'm new and using Swift 3. I read some question regarding with my question like this and this but it's not working. I think the Swift 2 is simpler than Swift 3 in handling for String. Any sugesstion or answer will help for me?
You can use index(of to check if the currNrexists in your array. (The class must conform to the Equatable protocol)
var arrayNr = ["1", "32", "100"]
let currNr = "32"
// Check to remove the existing element
if let index = arrayNr.index(of: currNr) {
arrayNr.remove(at: index)
}
arrayNr.append(currNr)
Say you have an array of string, namely type [String]. Now you want to remove a string if it exists. So you simply need to filter the array by this one line of code
stringArray= stringArray.filter(){$0 != "theValueThatYouDontWant"}
For example, you have array like this and you want to remove "1"
let array = ["1", "32"]
Simply call
array = array.filter(){$0 != "1"}
Long Solution
sampleArray iterates over itself and removes the value you are looking for if it exists before exiting the loop.
var sampleArray = ["Hello", "World", "1", "Again", "5"]
let valueToCheck = "World"
for (index, value) in sampleArray.enumerated() {
if value == valueToCheck && sampleArray.contains(valueToCheck) {
sampleArray.remove(at: index)
break
}
}
print(sampleArray) // Returns ["Hello", "1", "Again", "5"]
Short Solution
sampleArray returns an array of all values that are not equal to the value you are checking.
var sampleArray = ["Hello", "World", "1", "Again", "5"]
let valueToCheck = "World"
sampleArray = sampleArray.filter { $0 != valueToCheck }
print(sampleArray) // Returns ["Hello", "1", "Again", "5"]
Referring to: Swift Standard Library > Dictionary > map(_:)
Returns an array containing the results of mapping the given closure
over the sequence’s elements.
As mentioned, we can do mapping in dictionaries, but the output will be an array, not a "mapped" dictionary.
Honestly, I'm not pretty sure if saying "mapping the whole dictionary" is legal, but what I mean is the following:
Consider that we have:
let myDict = ["1": "one","2": "tow","3": "three"]
and we want to map the whole thing! (both keys and values). Output should be:
let mappedDict = ["03": "THREE", "02": "TOW", "01": "ONE"]
Let's assume that the goal of the mapping is to add "0" as a first character to all keys and let all values to be upper-cased.
To make it more readable, I posted a solution (what I tried) as an answer instead of mentioning it in the question it self; I think my answer is not so elegant (or at least how I feel about its code smell), I mapped the keys, the values and combine them in a dictionary, each step has been achieved independently.
So, What I am asking about is:
Is there a way to do this job directly in one step? Something similar to:
This snippet is a demonstration of what I'm asking about, code won't work fine
let myDict = ["1": "one","2": "tow","3": "three"]
let mappedDict = myDict.map { key, value in
"0" + key
value.uppercased()
}
Thanks in advance.
How about this?
let myDict = ["1": "one","2": "tow","3": "three"]
let mappedDict = myDict.reduce([:]) { (result, pair) -> [String: String] in
var result = result
result["0" + pair.key] = pair.value.uppercased()
return result
}
You can achieve this by doing the following:
let myDict = ["1": "one","2": "tow","3": "three"]
let mappedKeys = myDict.map { "0" + $0.key } // ["02", "01", "03"]
let mappedValues = myDict.map { $0.value.uppercased() } // ["TOW", "ONE", "THREE"]
var mappedDict = [String: String]()
let zippedArray = Array((zip(mappedKeys, mappedValues)))
for element in zippedArray {
mappedDict[element.0] = element.1
}
print(mappedDict) // ["03": "THREE", "02": "TOW", "01": "ONE"]
To be more clear, the above code snippet doing the following:
Mapping the dictionary keys.
Mapping the dictionary values.
Create a new empty dictionary mappedDict to append to it.
Combining mapped keys/values into zippedArray (using zip).
Filling mappedDict via for-loop.
I have a problem concerning my nested dictionary.
var level1Dictionary = [String : [String : String]]()
var ChosenDeckLabel = [String]
textview.text
I want to see if the dictionary contains a certain value within my, else if statement as such:
else if level1Dictionary[ChosenDeckLabel[textview.text]] != nil {
this returns error:
Cannot subscript value of type String with an index of type String!
How should I cast it to check if the nested dictionary contains the value?
Dictionaries are optionals by default because they are not sure if a key/value pair exist. Be sure to include your "!" AND "?" to wrap and unwrap your data being passed.
Arrays offer subscripting via integers and ranges as seen in Swift's API:
public subscript (index: Int) -> Element
public subscript (subRange: Range<Int>) -> ArraySlice<Element>
You're trying to subscript via a string which is throwing the error. You need to get the index of the element in the array and then use that to subscript the array to get the value: e.g.
let dictOfDicts = [String : [String : String]]()
var arrayOfStrings: [String] = ["a", "b", "c"]
let stringToCheck = "a"
dictOfDicts["a"] = ["some": "thing"]
if let index = array.indexOf(stringToCheck) {
if dictOfDicts[array[Int(index)]] != nil {
// do something
}
}
I think this is what you intend to do:
else if level1Dictionary[strIndex1][strIndex2] != nil {
When you are doing this:
level1Dictionary[ChosenDeckLabel[textview.text]]
you are trying to access the ChosenDeckLabel array using a String subscript:
ChosenDeckLabel[textview.text]
which is not a valid operation. Arrays are Int indexed and not string indexed.
An NSSet can be converted to Array using set.allObjects() but there is no such method in the new Set (introduced with Swift 1.2). It can still be done by converting Swift Set to NSSet and use the allObjects() method but that is not optimal.
You can create an array with all elements from a given Swift
Set simply with
let array = Array(someSet)
This works because Set conforms to the SequenceType protocol
and an Array can be initialized with a sequence. Example:
let mySet = Set(["a", "b", "a"]) // Set<String>
let myArray = Array(mySet) // Array<String>
print(myArray) // [b, a]
In the simplest case, with Swift 3, you can use Array's init(_:) initializer to get an Array from a Set. init(_:) has the following declaration:
init<S>(_ s: S) where S : Sequence, Element == S.Iterator.Element
Creates an array containing the elements of a sequence.
Usage:
let stringSet = Set(arrayLiteral: "car", "boat", "car", "bike", "toy")
let stringArray = Array(stringSet)
print(stringArray)
// may print ["toy", "car", "bike", "boat"]
However, if you also want to perform some operations on each element of your Set while transforming it into an Array, you can use map, flatMap, sort, filter and other functional methods provided by Collection protocol:
let stringSet = Set(["car", "boat", "bike", "toy"])
let stringArray = stringSet.sorted()
print(stringArray)
// will print ["bike", "boat", "car", "toy"]
let stringSet = Set(arrayLiteral: "car", "boat", "car", "bike", "toy")
let stringArray = stringSet.filter { $0.characters.first != "b" }
print(stringArray)
// may print ["car", "toy"]
let intSet = Set([1, 3, 5, 2])
let stringArray = intSet.flatMap { String($0) }
print(stringArray)
// may print ["5", "2", "3", "1"]
let intSet = Set([1, 3, 5, 2])
// alternative to `let intArray = Array(intSet)`
let intArray = intSet.map { $0 }
print(intArray)
// may print [5, 2, 3, 1]
I created a simple extension that gives you an unsorted Array as a property of Set in Swift 4.0.
extension Set {
var array: [Element] {
return Array(self)
}
}
If you want a sorted array, you can either add an additional computed property, or modify the existing one to suit your needs.
To use this, just call
let array = set.array
ADDITION :
Swift has no DEFINED ORDER for Set and Dictionary.For that reason you should use sorted() method to prevent from getting unexpected results such as your array can be like ["a","b"] or ["b","a"] and you do not want this.
TO FIX THIS:
FOR SETS
var example:Set = ["a","b","c"]
let makeExampleArray = [example.sorted()]
makeExampleArray
Result: ["a","b","c"]
Without sorted()
It can be:
["a","b","c"] or ["b","c","a",] or ["c","a","b"] or ["a","c","b"] or ["b","a","c"] or ["c","b","a"]
simple math : 3! = 6
The current answer for Swift 2.x and higher (from the Swift Programming Language guide on Collection Types) seems to be to either iterate over the Set entries like so:
for item in myItemSet {
...
}
Or, to use the "sorted" method:
let itemsArray = myItemSet.sorted()
It seems the Swift designers did not like allObjects as an access mechanism because Sets aren't really ordered, so they wanted to make sure you didn't get out an array without an explicit ordering applied.
If you don't want the overhead of sorting and don't care about the order, I usually use the map or flatMap methods which should be a bit quicker to extract an array:
let itemsArray = myItemSet.map { $0 }
Which will build an array of the type the Set holds, if you need it to be an array of a specific type (say, entitles from a set of managed object relations that are not declared as a typed set) you can do something like:
var itemsArray : [MyObjectType] = []
if let typedSet = myItemSet as? Set<MyObjectType> {
itemsArray = typedSet.map { $0 }
}
call this method and pass your set
func getArrayFromSet(set:NSSet)-> NSArray {
return set.map ({ String($0) })
}
Like This :
var letters:Set = Set<String>(arrayLiteral: "test","test") // your set
print(self.getArrayFromSet(letters))