Access to Dictionary values inside Array in swift - arrays

I have an array with data for my TableView sections
let menuItems : Dictionary<String,Dictionary<String,String>>[] = [
[
"data" : [
"name":"test",
"image":"test.png"
]
],
[
"data" : [
"name":"test2",
"image":"test2.png"
]
]
]
I'm trying to access it by using subscript
func tableView(tableView: UITableView!, titleForHeaderInSection section: Int) -> String! {
return menuItems[section]["data"]["name"]
}
and have the error
Could not find member 'subscript'
I have read a lot of similar questions on stackoverflow, but still haven't understood how to fix it. I tried to unwrap with "!" symbol and used another variables - no result.
Could you explain how does it works, please?

println(menuItems[0]["data"]!["name"])
Short answer: menuItems[0]["data"] returns an optional dictionary.
You can look at the below REPL output to understand the issue.
sets up the object.
shows that the array returns a regular dictionary. If the index is out of range, an error will be thrown instead of returning an optional dictionary.
shows that accessing a key in a dictionary that does not exist will return nil
shows that accessing a key in a dictionary will return an optional of its value type. For instance, if a dictionary is storing Strings in its values, it will return a String? when indexed into.
shows that we cannot use subscripts on an optional dictionary.
shows that we can force the optional dictionary to be a dictionary because we know it is not nil. The REPL shows us that the new return type is Dictionary<String, String>
shows that we can then use subscripts to get at the innermost values, and this returns an optional string to us. Optional strings are printable, so my code above doesn't need a second !.
shows that if we force both dictionary return types to their non-optional types, we get a regular value back (a string).
In real code, you would probably want to check for optionals, and handle nils accordingly.
1> let menu: Dictionary<String, Dictionary<String, String>>[] = [["dict1" : ["key" : "val"]], ["dict2" : ["key" : "val"]]]
menu: Dictionary<String, Dictionary<String, String>>[] = size=2 {
[0] = {
[0] = {
key = "dict1"
value = {
[0] = {
key = "key"
value = "val"
}
}
}
}
[1] = {
[0] = {
key = "dict2"
value = {
[0] = {
key = "key"
value = "val"
}
}
}
}
}
2> menu[0]
$R1: Dictionary<String, Dictionary<String, String>> = {
[0] = {
key = "dict1"
value = {
[0] = {
key = "key"
value = "val"
}
}
}
}
3> menu[0]["key"]
$R2: Dictionary<String, String>? = nil
4> menu[0]["dict1"]
$R3: Dictionary<String, String>? = Some {
[0] = {
key = "key"
value = "val"
}
}
5> menu[0]["dict1"]["key"]
REPL:6:1: error: could not find member 'subscript'
menu[0]["dict1"]["key"]
^~~~~~~~~~~~~~~~~~~~~~~
6> menu[0]["dict1"]!
$R4: Dictionary<String, String> = {
[0] = {
key = "key"
value = "val"
}
}
7> menu[0]["dict1"]!["key"]
$R5: String? = "val"
8> menu[0]["dict1"]!["key"]!
$R6: String = "val"

Related

How do I return an array?

I need to write an app that takes the string that is entered into a text field and use it to call a thesaurus function. The function must return an array that contains synonyms to the string, but I seem to be having some syntax issues. Can someone lend an extra pair of eyes?
I already checked on the syntax and the scope of the variables, but I don't seem to understand where I'm getting it wrong.
var synonymsDictionary = ["swift" : ["abrupt", "expeditious", "hasty", "nimble", "quick", "rapid", "speedy", "sudden", "unexpected"],
"objective" : ["detached", "disinterested", "dispassionate", "equitable", "evenhanded", "nonpartisan", "open-minded", "unbiased"],
"calculate" : ["adjust", "appraise", "consider", "count", "determine", "forecast", "gauge", "guess", "measure", "multiply", "reckon", "subtract", "tally", "weigh", "work out"],
"create" : ["build", "conceive", "constitute", "construct", "design", "devise", "discover", "establish", "forge", "form"]]
func synonyms(for term: String) -> String {
if let sameWords = synonymsDictionary[term] {
print("These are the synonyms for \(term): \(sameWords)")
} else {
print("This word doesn't have any synonyms.")
}
let result = synonymsDictionary[term]
return result
}
synonyms(for: "objective")
I should be getting an array with the synonyms for the term (string ) that I put in.Error
Cannot convert return expression of type '[String]?' to return type 'String'
Currently you return String but it should be an array [String] so try
func synonyms(for term: String) -> [String] {
return synonymsDictionary[term] ?? []
}
Also hold a reference to the returned value
let res = synonyms(for: "objective")
print(res)
Since synonymsDictionary is a dictionary [String:[String]] then every value is of type [String] that can't be returned in a function that returns a value of type String

Swift: Filter a dictionary key from a struct from an array, which is optional

struct Test {
var title: String
var message: [String?: String?]
init(title: String, message: [String?:String?]) {
self.title = title
self.message = message
}
}
var cases = [
Test(title: "1", message: ["tag1": nil]),
Test(title: "2", message: ["tag2": "preview2"]),
Test(title: "3", message: [nil:nil]),
Test(title: "4", message: ["tag1":"preview4"])
]
Now, I want:
An array with all keys from message property from cases - tag1 and tag2 (no nils in it). I just tried everything I know, I couldn't do it. Tried with filtering cases, got optionals.
There are no previews without a tag, so there is no need for an array with them. I only need a list with the tags, in order to sort it and show the relevant previews from the cases. That's why I need to know a way how to access these previews from the cases. Let's say in a UITableView:
cell.previewLabel?.text = cases[indexPath.row].preview[//No idea what here]
Of course, a dictionary with [tags: previews] would also be perfect!
Thanks in advance! I hope what I want is possible.
Here is an array that only contains elements from cases that have all keys and values not nil :
let filtered = cases.filter { test in
return test.message.allSatisfy({ entry in
return entry.key != nil && entry.value != nil
})
}
Or using the shorthand notation :
let filtered = cases.filter {
$0.message.allSatisfy({
$0.key != nil && $0.value != nil
})
}
With structs there is a default initializer, so you can write your Test struct this way:
struct Test {
var title: String
var message: [String?: String?]
}
It's not completely clear to me what you're attempting to do, however, this will filter your cases array to only Test objects that contain non-nil values in the message dictionary:
let nonNil = cases.filter { (test) -> Bool in
return Array(test.message.values).filter({ (value) -> Bool in
return value == nil
}).count <= 0
}
The variable nonNil now contains the Test objects where title is "2" and title is "4".
You could further filter that if you want a [tags:preview] dictionary. Something like this would do that:
let tags = nonNil.map( { $0.message} ).flatMap { $0 }.reduce([String:String]()) { (accumulator, current) -> [String:String] in
guard let key = current.key, let value = current.value else { return accumulator }
var accum = accumulator
accum.updateValue(value, forKey: key)
return accum
}
The tags dictionary now contains: ["tag1": "preview4", "tag2": "preview2"]

Find part of value in dictionary

I have the following dictionary:
["uno" : "unoValue", "dos" : "dosValue", "tres" : "tresValue"]
I need to do a each of the keys that contain certain characters. For example if I look for 'no' I would only get key "uno" because of the "uNOValue". If I look for 'val' I would get all three keys.
I would need to get all the keys in an array such as ["uno"] and ["uno","dos","tres"] for the second case.
I am new at swift but I am building a dummy app for a search and I have no idea how to do this. If you have any other ideas for the search function I will also accept them gratefully.
dict = {"uno" : "unoValue", "dos" : "dosValue", "tres" : "tresValue"}
search_term = "Val"
list = []
for key, value in dict.items():
if search_term in value:
list.append(key)
print (list)
Try this:
let dict = ["uno" : "unoValue", "dos" : "dosValue", "tres" : "tresValue"]
let searchTerm = "val"
let keys = Array(
dict.keys.filter { dict[$0]!.range(of: searchTerm, options: [.caseInsensitive]) != nil }
)
print(keys)
How it works:
dict.keys.filter filter all the keys in the dictionary meeting a certain condition
dict[$0]!.range(of: searchTerm, options: [.caseInsensitive]) != nil checks the value that it contains the search term
Array( ... ) converts it to an array of strings, otherwise it's a LazyFilterCollection
my Playground sample:
import Foundation
let dict = ["uno" : "unoValue", "dos" : "dosValue", "tres" : "tresValue"]
let searchTerm = "val"
var result = dict.filter {
$0.value.range(of: searchTerm, options: [.caseInsensitive]) != nil
}.map{$0.key}
print(result) // ["tres", "uno", "dos"]

How can I tell what the index of an item in a multidimensional array would be if the array were flattened?

I have a multidimensional array with some repeated elements in the deepest level:
[
["taco","burrito"],
["chalupa","taco","bread"]
["pizza","lasagna"],
["pizza","taco","burrito"]
["salad","sandwich"],
["meat","turkey"]
["cups","chicken"],
["rabbit","taco", "chicken", "salad"]
]
I have flattened this array into...
[
"taco",
"burrito",
"chalupa",
"taco",
"bread",
"pizza",
"lasagna",
etc...
]
I have found the second occurrence of the word "taco" in the multidimensional array and know its index.
How can I translate that index to an index in the flattened array?
So in this example it would be...
multiDimensionalArray[0][1][1]
=
flatArray[3]
Swift doesn't have native support for flattening arrays. You could roll this out yourself, or you could use ExSwift which has a flatten method. If you flatten the array successfully, obviously you could then determine which index the value is at using indexOfObject. Since you have multiple identical objects, you may want to use indexesOfObjectsPassingTest, which will give you an index set for all indexes matching the test you provide.
First of all you need a function to flatten the array into an array of strings in order to gain the true indices of the elements.
public func flattenArray(array: Array<AnyObject>) -> Array<String> {
var flattened: Array<String> = []
for object: AnyObject in array {
if object is Array<AnyObject> {
for string in flattenArray(object as Array<AnyObject>) {
flattened.append(string)
}
} else if object is String {
flattened.append(object as String)
}
}
return flattened
}
Then you need to write a function that will obtain the index of an element in an array at a certain number of occurrences
public func indexOfString(string: String, inArray array: Array<String>, occurance: Int = 1) -> Int? {
var occurancesSoFar = 0
var index: Int? = nil
var currentIndex = 0
for object in array {
if object == string {
if (++occurancesSoFar == occurance) {
index = currentIndex
}
}
++currentIndex
}
return index
}
Calling the above functions in order gives the correct index value:
let array = [
["taco","burrito"],
["chalupa","taco","bread"],
["pizza","lasagna"],
["pizza","taco","burrito"],
["salad","sandwich"],
["meat","turkey"],
["cups","chicken"],
["rabbit","taco", "chicken", "salad"]
]
let flat = flattenArray(array)
let index = indexOfString("taco", inArray: flat, occurance: 2)
println(index) // Optional(3)
Using these methods you could encapsulate them into a single function perhaps called
func flattenAndFindElement(element: String,
inArray array: Array<AnyObject>,
atOccurrence occurrence: Int) -> Int?
or something similar.
Hope that helps answer your question.
Here is what I ended up doing:
func findFlattenedIndex(array: Array<Array<String>>, firstIndex: Int, secondIndex: Int) -> Int {
var flatIndex = 0
for (index1,secondTier) in enumerate(array) {
for element in secondTier {
if index1 != firstIndex{
println(element)
flatIndex += 1
}
}
if index1 == firstIndex {
flatIndex += secondIndex
return flatIndex
break
}
}
return flatIndex
}
Of course this could be modified to deal with any number of dimensions.
Thanks for everyone's help with this.

Arrays and Swift

I'm trying to deal with an app in Swift and I'm facing the following error using arrays:
fatal error: Array index out of range
This appears when apps assign a value to the array at index 0:
class DrawScene: SKScene {
init(size: CGSize) {
super.init(size: size)
}
var line = SKShapeNode()
var path = CGPathCreateMutable()
var touch: UITouch!
var pts = [CGPoint]()
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
/* Called when a touch begins */
touch = touches.anyObject() as UITouch!
self.pts[0] = touch.locationInNode(self) <-- Error appears here
drawLine()
}
Some ideas? (I'm using xcode 6 Beta 4)
Your array is empty at first.
if self.pts.isEmpty {
self.pts.append(touch.locationInNode(self))
}
else {
self.pts[0] = touch.locationInNode(self)
}
As it says in the docs:
“You can’t use subscript syntax to append a new item to the end of an array. If you try to use subscript syntax to retrieve or set a value for an index that is outside of an array’s existing bounds, you will trigger a runtime error.”
You'll have to use append or += to add to an empty array. If you always want to set this point to the first object in the array, replacing anything already there, you'll have to check the count of the array first.
I don't recommend this to anyone, but you could implement your own subscript that allows you to do this:
extension Array {
subscript(safe index: Int) -> Element? {
get {
return self.indices.contains(index) ? self[index] : nil
}
set(newValue) {
guard let newValue = newValue { return }
if self.count == index {
self.append(newValue)
} else {
self[index] = newValue
}
}
}
}
And you can use this as expected:
var arr = [String]()
arr[safe: 0] = "Test"
arr[safe: 1] = "Test2"
print(arr) // ["Test", "Test2"]
arr[safe: 0] = "Test0"
print(arr) // ["Test0", "Test2"]
arr[safe: 3] = "Test2" // fatal error: Index out of range

Resources