Swift Xcode version 13.2.1
Here we have two Arrays,(1)var dicSearch=String and (2)var searchingDic: [[String: Any]]=[] I want to assign searchingDic to dicSearch when i implement it than it show error like, Cannot assign value of type '[String]' to type '[[String : Any]]'
here's my code, please anyone help!
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
searchingDic = dicSearch.filter{filtering in
let filterService = filtering["fName"] as? String
return((filterService?.lowercased().contains(searchText.lowercased()))!)
}
It looks like you are trying to create a filtered list based on a search term, but your dicSearch type is an array of strings (i.e: [String]), while your searchingDic is an array of dictionaries (i.e: [[String : Any]]).
This might be confusing when coming from a different language, but in Swift, the following declaration is a dictionary:
var dict: [String: Any] = [
"key1": "value1",
"key2": "value2",
]
so the following:
var arrayOfdicts: [[String: Any]] = [
["foo": "bar"],
["apples": "oranges"],
dict
]
is actually an array, containing a list of dictionaries, notice how I've put the dict declared above in the second array.
The compiler is telling you that you cannot assign a '[String]' to type '[[String : Any]]'
because this:
// example to an array of strings
var fullList: [String] = [
"apples",
"bananas",
"cucumbers"
]
// is not the same as
var arrayOfdicts: [[String: Any]] = [
["foo": "bar"],
["apples": "oranges"],
dict
]
The Array#filter method, iterates the array itself, and returns a new array with only the elements that return true in the return statement.
so either both your arrays need to be [String] or both your arrays need to be [[String:Any]]
example for String arrays:
// array
var fullList: [String] = [
"apples",
"bananas",
"cucumbers"
]
var filteredList: [String] = []
var searchTerm = "b"
filteredList = fullList.filter{ item in
let value = item
return value.lowercased().contains(searchTerm)
}
print(filteredList) // prints ["bananas", "cucumbers"]
an example for filtering with array of dictionaries:
var people: [[String: Any]] = [
["name": "Joe"],
["name": "Sam"],
["name": "Natalie"],
["name": "Eve"]
]
var filteredPeople: [[String: Any]] = []
var nameFilter = "a"
filteredPeople = people.filter{ item in
let value = item["name"] as! String
return value.lowercased().contains(nameFilter)
}
print(filteredPeople) // prints [["name": "Sam"], ["name": "Natalie"]]
Hope this helps :)
I have a collection view made up of 4 sections.
Each section is an array of strings, like this:
var picList: [String] = ["Bird", "Cat", "Cow", "Dog", "Duck", "Elephant", "Fish", "Giraffe", "Lion", "Mouse", "Sheep", "Snake" ]
var vehiclesList: [String] = ["Airplane", "Ambulance", "Boat", "Bus", "Car", "Fire_Engine", "Helicopter", "Motorcycle", "Tank", "Tractor", "Train", "Truck" ]
var fruitsList: [String] = ["Apple", "Banana", "Grapes", "Mango", "Orange", "Peach", "Pineapple", "Strawberry", "Watermelon" ]
var bodyPartsList: [String] = ["Arm", "Ear", "Eye", "Face", "Feet", "Hand", "Hair", "Legs", "Mouth", "Nose" ]
I created a UITapGestureRecognizer for the cell, that when I click on the cell, I get the index.
Here is the tapMethod
func handleTap(sender: UITapGestureRecognizer) {
print("tap")
if let indexPath = self.collectionView?.indexPathForItem(at: sender.location(in: self.collectionView)) {
let cell = collectionView?.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! PicViewCell
print("index path: + \(indexPath)")
} else {
print("")
}
}
Now the index prints like this [0,0] (if I press on the first item, ie bird). [2,4] if I press on the 4th item in the 2nd secion (ie mango). But I don't know how to translate the index, to get the corresponding string value. I mean something like this:
var itemName: String = [2,4] (and itemName would be mango)
I'm new to swift, so any help would be great.
Thanks so much.
What you need is an array of arrays, aka a 2D array - [[String]].
var array = [picList, vehicleList, fruitsList, bodyPartsList]
Then, you can access this with 2 indices like this:
var itemName = array[2][4]
You can create a two dimensional array, and add there your lists, indexpath.section will be the index of list and indexpath.item will be the index in thst list
I have a large array and need to access it by a key (a lookup) so I need to create Dictionary. Is there a built in function in Swift 3.0 to do so, or do I need to write it myself?
First I will need it for a class with key "String" and later on maybe I will be able to write a template version for general purpose (all types of data and key).
Note for 2019. This is now simply built-in to Swift 5, uniqueKeysWithValues and similar calls.
Is that it (in Swift 4)?
let dict = Dictionary(uniqueKeysWithValues: array.map{ ($0.key, $0) })
Note:
As mentioned in the comment, using uniqueKeysWithValues would give a fatal error (Fatal error: Duplicate values for key: 'your_key':) if you have duplicated keys.
If you fear that may be your case, then you can use init(_:uniquingKeysWith:) e.g.
let pairsWithDuplicateKeys = [("a", 1), ("b", 2), ("a", 3), ("b", 4)] // or `let pairsWithDuplicateKeys = array.map{ ($0.key, $0) }`
let firstValues = Dictionary(pairsWithDuplicateKeys, uniquingKeysWith: { (first, _) in first })
print(firstValues)
//prints ["a": 1, "b": 2]
let lastValues = Dictionary(pairsWithDuplicateKeys, uniquingKeysWith: { (_, last) in last })
print(lastValues)
//prints ["a": 3, "b": 4]
On Swift 4, you can achieve this by using Dictionary's grouping:by: initializer
For ex:
You have class named A
class A {
var name: String
init(name: String) {
self.name = name
}
// .
// .
// .
// other declations and implementions
}
Next, you have an array of objects of type A
let a1 = A(name: "Joy")
let a2 = A(name: "Ben")
let a3 = A(name: "Boy")
let a4 = A(name: "Toy")
let a5 = A(name: "Tim")
let array = [a1, a2, a3, a4, a5]
Let's say you want to create a Dictionary by grouping all the names by their first letter. You use Swifts Dictionary(grouping:by:) to achieve this
let dictionary = Dictionary(grouping: array, by: { $0.name.first! })
// this will give you a dictionary
// ["J": [a1], "B": [a2, a3], "T": [a4, a5]]
source
Note however that the resulting Dictionary "dictionary" is of type
[String : [A]]
it is not of type
[String : A]
as you may expect. (Use #uniqueKeysWithValues to achieve the latter.)
I think you're looking for something like this:
extension Array {
public func toDictionary<Key: Hashable>(with selectKey: (Element) -> Key) -> [Key:Element] {
var dict = [Key:Element]()
for element in self {
dict[selectKey(element)] = element
}
return dict
}
}
You can now do:
struct Person {
var name: String
var surname: String
var identifier: String
}
let arr = [Person(name: "John", surname: "Doe", identifier: "JOD"),
Person(name: "Jane", surname: "Doe", identifier: "JAD")]
let dict = arr.toDictionary { $0.identifier }
print(dict) // Result: ["JAD": Person(name: "Jane", surname: "Doe", identifier: "JAD"), "JOD": Person(name: "John", surname: "Doe", identifier: "JOD")]
If you'd like your code to be more general, you could even add this extension on Sequence instead of Array:
extension Sequence {
public func toDictionary<Key: Hashable>(with selectKey: (Iterator.Element) -> Key) -> [Key:Iterator.Element] {
var dict: [Key:Iterator.Element] = [:]
for element in self {
dict[selectKey(element)] = element
}
return dict
}
}
Do note, that this causes the Sequence to be iterated over and could have side effects in some cases.
As others already said, we need to understand which are the keys.
However I am trying to provide a solution to my interpretation of your question.
struct User {
let id: String
let firstName: String
let lastName: String
}
Here I am assuming that 2 users with the same id cannot exist
let users: [User] = ...
let dict = users.reduce([String:User]()) { (result, user) -> [String:User] in
var result = result
result[user.id] = user
return result
}
Now dict is a dictionary where the key is the user id and the value is the user value.
To access a user via its id you can now simply write
let user = dict["123"]
Update #1: General approach
Given an array of a given type Element, and a closure that determine the key of an Element, the following generic function will generate a Dictionary of type [Key:Element]
func createIndex<Key, Element>(elms:[Element], extractKey:(Element) -> Key) -> [Key:Element] where Key : Hashable {
return elms.reduce([Key:Element]()) { (dict, elm) -> [Key:Element] in
var dict = dict
dict[extractKey(elm)] = elm
return dict
}
}
Example
let users: [User] = [
User(id: "a0", firstName: "a1", lastName: "a2"),
User(id: "b0", firstName: "b1", lastName: "b2"),
User(id: "c0", firstName: "c1", lastName: "c2")
]
let dict = createIndex(elms: users) { $0.id }
// ["b0": {id "b0", firstName "b1", lastName "b2"}, "c0": {id "c0", firstName "c1", lastName "c2"}, "a0": {id "a0", firstName "a1", lastName "a2"}]
Update #2
As noted by Martin R the reduce will create a new dictionary for each iteration of the related closure. This could lead to huge memory consumption.
Here's another version of the createIndex function where the space requirement is O(n) where n is the length of elms.
func createIndex<Key, Element>(elms:[Element], extractKey:(Element) -> Key) -> [Key:Element] where Key : Hashable {
var dict = [Key:Element]()
for elm in elms {
dict[extractKey(elm)] = elm
}
return dict
}
let pills = ["12", "34", "45", "67"]
let kk = Dictionary(uniqueKeysWithValues: pills.map{ ($0, "number") })
["12": "number", "67": "number", "34": "number", "45": "number"]
swift5 swift4
The following converts an array to a dictionary.
let firstArray = [2,3,4,5,5]
let dict = Dictionary(firstArray.map { ($0, 1) } , uniquingKeysWith: +)
Swift 5
extension Array {
func toDictionary() -> [Int: Element] {
self.enumerated().reduce(into: [Int: Element]()) { $0[$1.offset] = $1.element }
}
}
This extension works for all sequences (including arrays) and lets you select both key and value:
extension Sequence {
public func toDictionary<K: Hashable, V>(_ selector: (Iterator.Element) throws -> (K, V)?) rethrows -> [K: V] {
var dict = [K: V]()
for element in self {
if let (key, value) = try selector(element) {
dict[key] = value
}
}
return dict
}
}
Example:
let nameLookup = persons.toDictionary{($0.name, $0)}
Just do it simply,
let items = URLComponents(string: "https://im.qq.com?q=13&id=23")!.queryItems!
var dic = [String: Any?]()
items.foreach {
dic[$0.name] = $0.value
}
reduce is not very suitable,
let dic: [String: Any?] = items.reduce([:]) { (result: [String: Any?], item: URLQueryItem) -> [String: Any?] in
var r = result
r[item.name] = item.value // will create an copy of result!!!!!!
return r
}
As i understand from you're question you would like to convert to Array to Dictionary.
In my case i create extension for the Array and keys for the dictionary will be indexes of the Array.
Example:
var intArray = [2, 3, 5, 3, 2, 1]
extension Array where Element: Any {
var toDictionary: [Int:Element] {
var dictionary: [Int:Element] = [:]
for (index, element) in enumerate() {
dictionary[index] = element
}
return dictionary
}
}
let dic = intArray.toDictionary
Compatible with Swift 5 Standard Library (Xcode 10.2+ , iOS 12.2).
Here's an example of usage of an initializer init(uniqueKeysWithValues:)
The input let array: [String] = Locale.isoRegionCodes is an array of ISO31661-2 codes represented by a string.
let countryCodeAndName: [String: String] = Dictionary(uniqueKeysWithValues: Locale.isoRegionCodes.map { ($0, Locale.current.localizedString(forRegionCode: $0) ?? "")} )
Returned dictionary, will list all regions with ISO31661-2 code as a key and a localized region name as a value.
Output:
...
"PL":"Poland"
"DE":"Germany"
"FR":"France"
"ES":"Spain"
...
Example 2:
let dictionary: [String: String] = Dictionary(uniqueKeysWithValues: [ ("key1", "value1"), ("key2", "value2")] )
Output:
["key1": "value1", "key2": "value2"]
Important:
Precondition: The sequence must not have duplicate keys.
Code below will crash an app:
let digitWords = ["one", "two", "three", "four", "five", "five"]
let wordToValue = Dictionary(uniqueKeysWithValues: zip(digitWords, 1...6))
with:
Fatal error: Duplicate values for key: 'five'
If you want to follow the pattern set out by map and reduce in swift you could do something nice and functional like this:
extension Array {
func keyBy<Key: Hashable>(_ keyFor: (Element) -> Key) -> [Key: Element] {
var ret = [Key: Element]()
for item in self{
ret[keyFor(item)] = item
}
return ret
}
}
Usage:
struct Dog {
let id: Int
}
let dogs = [Dog(id: 1), Dog(id: 2), Dog(id: 3), Dog(id: 4)]
let dogsById = dogs.keyBy({ $0.id })
// [4: Dog(id: 4), 1: Dog(id: 1), 3: Dog(id: 3), 2: Dog(id: 2)]
Swift way:
extension Sequence {
func toDictionary<Key: Hashable>(with selectKey: (Element) -> Key) -> [Key: Element] {
reduce(into: [:]) { $0[selectKey($1)] = $1 }
}
}
// let arr = [Person(id: 1, name: "Alan")]
// arr.toDictionary { $0.id }
// ==
// [1: Person(id: 1, name: "Alan")]
In the Playground example below I'm trying to modify a multidimensional NSMutableDictionary. Can someone please explain the correct way to modify a multidimensional mutable dictionary?
import Cocoa
let player = "Player 1"
let characterName = "Magic Glop"
let strength = 23
let defense = 220
let type = "fire"
var example: NSMutableDictionary = ["id":1,
"player":player,
"characters":
["character-name":characterName,
"stats":
["strength":strength,
"defense":defense,
"type":type
]
]
]
// My first attempt to update character-name.
example["characters"]!["character-name"] = "New Name"
// Error: Cannot assign to immutable expression of type 'AnyObject?!'
// Next I tried updating the value of "characters">"stats">"type" with .setObject
example["characters"]!["stats"]!!.setObject("water", forKey: "type")
// Documentation: .setObject adds a given key-value pair to the dictionary. If the key already exists in the dictionary, the object takes its place.
// Error: Execution was interrupted, reason: EXC_BAD INSTRUCTION (code=EXC_i386_INVOP, suncode=0x0).
Thanks in advance!
Class approach: You can update var to let for non-editable parameters.
class Player {
let id: Int
var name: String
var characters = [Character]()
init(id: Int, name: String) {
self.id = id
self.name = name
}
/**
Add new char to Player
*/
func addNewCharacter(new: Character) {
self.characters.append(new)
}
}
class Character {
var name: String
var strength: Int
var defense: Int
var type: String
init(name: String, strength: Int, defense: Int, type: String) {
self.name = name
self.strength = strength
self.defense = defense
self.type = type
}
}
func createPlayer() {
let player1 = Player(id: 1, name: "Bodrum")
// create new char and add to the player1 named Bodrum
let char1 = Character(name: "Magic Glop", strength: 23, defense: 220, type: "fire")
player1.addNewCharacter(char1)
print("old name:\(player1.characters[0].name), old type:\(player1.characters[0].type)")
// update char1's parameters
player1.characters[0].name = "Yalikavak"
player1.characters[0].type = "water"
print("new name:\(player1.characters[0].name), new type:\(player1.characters[0].type)")
}
// old name:Magic Glop, old type:fire
// new name:Yalikavak, new type:water
check this answer.
let player = "Player 1"
let characterName = "Magic Glop"
let strength = 23
let defense = 220
let type = "fire"
var example: [String: AnyObject] = ["id":1,
"player":player,
"characters":
["character-name":characterName,
"stats":
["strength":strength,
"defense":defense,
"type":type
]
]
]
print(example)
var charDic = example["characters"] as! [String: AnyObject]
charDic["character-name"] = "New Name" // change value for key character-name
var statsDic = charDic["stats"] as! [String: AnyObject]
statsDic["type"] = "water" // change the value for type
charDic["stats"] = statsDic // assign updated dic for key stats
example["characters"] = charDic // assign updated dic for key chacracters
print(example)
I am really not getting this - why is this not working?
var listOfFruit = ["Apple", "Banana","Lemon"]
var emptyDict = [String: String]()
var key = ["Name of Fruit","Name of Fruit","Name of Fruit"]
func createDictionary(){
var index: Int
index = listOfFruit.count
for index in listOfFruit {
emptyDict = [key[index]:listOfFruit[index]]
print (emptyDict)
}
}
I am getting the usual :
I'm trying to guess what you need because your question is pretty unclear.
IF given this input
var fruits = ["Apple", "Banana","Lemon"]
var keys = ["Name of Fruit", "Name of Fruit", "Name of Fruit"]
you want this output
["Name of Fruit 2": "Lemon", "Name of Fruit 0": "Apple", "Name of Fruit 1": "Banana"]
Then you can use this code
let dict = zip(fruits, keys).enumerate().reduce([String:String]()) { (var result, elm) -> [String:String] in
let key = "\(elm.element.1) \(elm.index)"
let value = elm.element.0
result[key] = value
return result
}
or this code
assert(keys.count == fruits.count)
var dict = [String:String]()
for i in 0..<fruits.count {
let key = "\(keys[i]) \(i)"
let value = fruits[i]
dict[key] = value
}