Swift dictionary with array as value - ios

How do you declare a dictionary that has an array as the value? Is this even possible?

Yes
let myDictionary: [String: [Int]] = ["Hello": [1, 2, 3], "World": [4, 5, 6]]
In fact, you don't even need the explicit type declaration if you assign an initial value in place. It can go as simple as:
let myDictionary = ["Hello": [1, 2, 3], "World": [4, 5, 6]]
To use the value:
println(myDictionary["Hello"][0]) // Print 1
println(myDictionary["World"][0]) // Print 4

var dictionary : [String:[AnyObject]]
var dictionary2 = [String:[AnyObject]]()
You can change AnyObject for any class or use it like AnyObject itself if you don't know the class that will be in the array.

If you want to store for example an array of strings:
var dict: [String: [String]]
or without syntactic sugar:
var dict: Dictionary<String, Array<String>>
Dictionaries, like arrays and more generally whatever uses generics, can handle anything that is a swift type, including tuples, closures, dictionaries, dictionaries of dictionaries, arrays of dictionaries, etc. - unless conditions are specified for the generic type (for instance, a dictionary key can be any type that implements the Hashable protocol), and in that case types must conform to the constraints.

Related

Combining two arrays of different types [duplicate]

I'm trying to find the best way to merge Swift arrays, that are not of same type, but they have same superclass. I've already read all the tutorials so I know I can use:
var array = ["Some", "Array", "of", "Strings"]
array += ["Another string", "and one more"]
array.append(["Or", "This"])
In this case array variable infers the type [String]. The problem I have relates to the next hypothetical situation with classes (properties do not matter in this case, just there to make a difference between classes):
class A {
var property1 : String?
}
class B : A {
var property2: String?
}
class C : A {
var property3: String?
}
So I create an array of class A and B objects:
var array = [ A(), B(), A(), B() ]
This array should now be of type [A], since this is the inferred type by both A and B classes.
Now I want to append objects to this array, that are of type C.
var anotherArray = [ C(), C(), C() ]
Since anotherArray is now of type [C], we should still be able to append it, since all C instances respond to A methods. So we try:
array += anotherArray
This fails due to:
Binary operator '+=' cannot be applied to operands of type '[A]' and '[C]'.
Similar story with using append method. While this does make sense, I cannot understand why this couldn't work.
Can someone explain why this is not working? What is the best solution to this problem?
The only sensible solution I found is to define the type of anotherArray to be [A], but are there any better ones or this is correct?
var anotherArray : [A] = [ C(), C(), C() ]
Thanks!
If C inherits from A then you can "upcast" an array of type [C] to an array of type [A]
array += anotherArray as [A]
Alternatively, use (tested with Swift 4)
array.append(contentsOf: anotherArray)
In addition to Martin's answer, you could create a protocol that all of the classes conform to and then when creating your array, make it's type that protocol.
Then you can add any of the classes to it without casting.
you can merge almost everything. the only requirement is that all elements of resulting array must conform to the same protocol.
let arr1 = [1,2,3] // [Int]
let arr2 = [4.0,5.0,6.0] // [Double]
let arr3 = ["a","b"] // [String]
import Foundation // NSDate
let arr4 = [NSDate()] // [NSDate]
// the only common protocol in this case is Any
var arr:[Any] = []
arr1.forEach { arr.append($0) }
arr2.forEach { arr.append($0) }
arr3.forEach { arr.append($0) }
arr4.forEach { arr.append($0) }
print(arr) // [1, 2, 3, 4.0, 5.0, 6.0, "a", "b", 2016-02-15 08:25:03 +0000]

Swift add object in to AnyObject

I need add an object into AnyObject
let swiftArray: [String: AnyObject] = ["one": "asd", "two": "asd", "three": "asd"]
How to add more objects?
Like,
let swiftArray: [String: AnyObject] = ["one": "asd", "two": "asd", "three": "asd"]
print(swiftArray)
swiftArray.append("test": "test") // NOT WORKING
Swift syntax is rather confusing. You are actually trying to create a Dictionary instead.
To use an Array:
var swiftArray: [String] = ["one", "two", "three"]
swiftArray.append("test")
--> Notice that you must use var instead of let for the Array to be mutable.
To use a Dictionary:
var swiftDict: [String: String] = ["one": "value"]
swiftDict["newElement"] = "newValue"
Please throughly read the Swift 2.1 documentation here.
You should probably read up on how swift works!!!
I'm gonna hold your hand on this one, and show you all the things that are off here.
FIRST, you're declaring your variable with a let statement, which creates Immutable variables (i.e., they can't change!). If you'd like to create a variable that can change, user var ..., so
var swiftDict: [String: AnyObject] = ["one": "asd", "two": "asd", "three": "asd"]
SECOND, what you're thinking of here is a dictionary, not an array. Arrays can be thought of as lists, or stacks of data, where each piece of data has a number, and is stacked on the last piece of data. A dictionary is more like a pool of data, where each piece has a name, or key, you call to retrieve it.
Rule of thumb, if you're got values tied together, you've got a dictionary.
THIRD, it's really easy to add to a dict, just use the following syntax.
swiftDict["four"] = "asd"
If you're still confused, try google, and feel free to ask for more info.

Swift: Declaring empty tuples

What's the correct way to declare an empty tuple?
For Arrays: var myArr : [String] = []
For tuples: var myTuple: (key: String, val: Int) = () ?
Is there a correct way to achieve this?
There's no such thing as an "unfilled" tuple value. In other words you don't create an empty tuple and then add values to it later. It's important to remember that tuples aren't collections like Array or Dictionary. Tuples are structured types. For example, you can't iterate through a tuple with a for loop. In your example, myTuple is a single value that happens to contain a String and an Int.
A tuple is like an on-demand unnamed structure, such as the following struct but if it were possible for it to be unnamed:
struct MyStruct {
let key: String
let val: Int
}
If you want to model a missing tuple value, you should make the type of the entire tuple optional. For example:
var myTuple: (key: String, val: Int)? = nil

Ordered map in Swift

Is there any built-in way to create an ordered map in Swift 2? Arrays [T] are sorted by the order that objects are appended to it, but dictionaries [K : V] aren't ordered.
For example
var myArray: [String] = []
myArray.append("val1")
myArray.append("val2")
myArray.append("val3")
//will always print "val1, val2, val3"
print(myArray)
var myDictionary: [String : String] = [:]
myDictionary["key1"] = "val1"
myDictionary["key2"] = "val2"
myDictionary["key3"] = "val3"
//Will print "[key1: val1, key3: val3, key2: val2]"
//instead of "[key1: val1, key2: val2, key3: val3]"
print(myDictionary)
Are there any built-in ways to create an ordered key : value map that is ordered in the same way that an array is, or will I have to create my own class?
I would like to avoid creating my own class if at all possible, because whatever is included by Swift would most likely be more efficient.
You can order them by having keys with type Int.
var myDictionary: [Int: [String: String]]?
or
var myDictionary: [Int: (String, String)]?
I recommend the first one since it is a more common format (JSON for example).
Just use an array of tuples instead. Sort by whatever you like. All "built-in".
var array = [(name: String, value: String)]()
// add elements
array.sort() { $0.name < $1.name }
// or
array.sort() { $0.0 < $1.0 }
"If you need an ordered collection of key-value pairs and don’t need the fast key lookup that Dictionary provides, see the DictionaryLiteral type for an alternative." - https://developer.apple.com/reference/swift/dictionary
You can use KeyValuePairs,
from documentation:
Use a KeyValuePairs instance when you need an ordered collection of key-value pairs and don’t require the fast key lookup that the Dictionary type provides.
let pairs: KeyValuePairs = ["john": 1,"ben": 2,"bob": 3,"hans": 4]
print(pairs.first!)
//prints (key: "john", value: 1)
if your keys confirm to Comparable, you can create a sorted dictionary from your unsorted dictionary as follows
let sortedDictionary = unsortedDictionary.sorted() { $0.key > $1.key }
As Matt says, dictionaries (and sets) are unordered collections in Swift (and in Objective-C). This is by design.
If you want you can create an array of your dictionary's keys and sort that into any order you want, and then use it to fetch items from your dictionary.
NSDictionary has a method allKeys that gives you all the keys of your dictionary in an array. I seem to remember something similar for Swift Dictionary objects, but I'm not sure. I'm still learning the nuances of Swift.
EDIT:
For Swift Dictionaries it's someDictionary.keys
You can use the official OrderedDictionary from the original Swift Repo
The ordered collections currently contain:
Ordered Dictionary (That you are looking for)
Ordered Set
They said it is going to be merged in the Swift itself soon (in WWDC21)
Swift does not include any built-in ordered dictionary capability, and as far as I know, Swift 2 doesn't either
Then you shall create your own. You can check out these tutorials for help:
http://timekl.com/blog/2014/06/02/learning-swift-ordered-dictionaries/
http://www.raywenderlich.com/82572/swift-generics-tutorial
I know i am l8 to the party but did you look into NSMutableOrderedSet ?
https://developer.apple.com/reference/foundation/nsorderedset
You can use ordered sets as an alternative to arrays when the order of
elements is important and performance in testing whether an object is
contained in the set is a consideration—testing for membership of an
array is slower than testing for membership of a set.
var orderedDictionary = [(key:String, value:String)]()
As others have said, there's no built in support for this type of structure. It's possible they will add an implementation to the standard library at some point, but given it's relatively rare for it to be the best solution in most applications, so I wouldn't hold your breath.
One alternative is the OrderedDictionary project. Since it adheres to BidirectionalCollection you get most of the same APIs you're probably used to using with other Collection Types, and it appears to be (currently) reasonably well maintained.
Here's what I did, pretty straightforward:
let array = [
["foo": "bar"],
["foo": "bar"],
["foo": "bar"],
["foo": "bar"],
["foo": "bar"],
["foo": "bar"]
]
// usage
for item in array {
let key = item.keys.first!
let value = item.values.first!
print(key, value)
}
Keys aren't unique as this isn't a Dictionary but an Array but you can use the array keys.
use Dictionary.enumerated()
example:
let dict = [
"foo": 1,
"bar": 2,
"baz": 3,
"hoge": 4,
"qux": 5
]
for (offset: offset, element: (key: key, value: value)) in dict.enumerated() {
print("\(offset): '\(key)':\(value)")
}
// Prints "0: 'bar':2"
// Prints "1: 'hoge':4"
// Prints "2: 'qux':5"
// Prints "3: 'baz':3"
// Prints "4: 'foo':1"

Swift: different objects in one array?

Is there a possibility to have two different custom objects in one array?
I want to show two different objects in a UITableView and I think the easiest way of doing this is to have all objects in one array.
Depending on how much control you want over the array, you can create a protocol that both object types implement. The protocol doesn't need to have anything in it (would be a marker interface in Java, not sure if there is a specific name in Swift). This would allow you to limit the array to only the object types you desire. See the sample code below.
protocol MyType {
}
class A: MyType {
}
class B: MyType {
}
var array = [MyType]()
let a = A()
let b = B()
array.append(a)
array.append(b)
If you know the types of what you will store beforehand, you could wrap them in an enumeration. This gives you more control over the types than using [Any/AnyObject]:
enum Container {
case IntegerValue(Int)
case StringValue(String)
}
var arr: [Container] = [
.IntegerValue(10),
.StringValue("Hello"),
.IntegerValue(42)
]
for item in arr {
switch item {
case .IntegerValue(let val):
println("Integer: \(val)")
case .StringValue(let val):
println("String: \(val)")
}
}
Prints:
Integer: 10
String: Hello
Integer: 42
You can use AnyObject array to hold any kind of objects in the same array:
var objectsArray = [AnyObject]()
objectsArray.append("Foo")
objectsArray.append(2)
// And also the inmutable version
let objectsArray: [AnyObject] = ["Foo", 2]
// This way you can let the compiler infer the type
let objectsArray = ["Foo", 2]
You can use the "type" AnyObject which allows you to store objects of different type in an array. If you also want to use structs, use Any:
let array: [Any] = [1, "Hi"]

Resources