Swift dictionary put [UInt8] as a value does not work - ios

I create a dictionary in Swift:
var type:String
var content:[UInt8]
let dict = NSMutableDictionary()
dict.setValue(type, forKey: "type")
dict.setValue(content, forKey: "content")
I get an error: Cannot convert value of type [UInt8] to expected argument type 'AnyObject?', but if I change the content type as [UInt], it will work fine. Why?
In fact, I want to define a byte array as in Java, so I want to use [UInt8], anyone could help me?

you can use Swift native types
var dict: Dictionary<String,Array<UInt8>> = [:]
dict["first"]=[1,2,3]
print(dict) // ["first": [1, 2, 3]]
i recommend you to use native Swift type as much as you can ... Please, see Martins's notes to you question, it is very useful!
if the case is you want to store there any value, just define you dictionary as the proper type
var dict: Dictionary<String,Array<Any>> = [:]
dict["first"]=[1,2,3]
class C {
}
dict["second"] = ["alfa", Int(1), UInt(1), C()]
print(dict) // ["first": [1, 2, 3], "second": ["alfa", 1, 1, C]]
to see, that the type of the value is still well known, you can check it
dict["second"]?.forEach({ (element) -> () in
print(element, element.dynamicType)
})
/*
alfa String
1 Int
1 UInt
C C
*/
if you want to store Any value, you are free to do it ...
var type:String = "test"
var content:[UInt8] = [1,2,3,4]
var dict: Dictionary<String,Any> = [:]
dict["type"] = type
dict["content"] = content
dict.forEach { (element) -> () in // ["content": [1, 2, 3, 4], "type": "test"]
print("key:", element.0, "value:", element.1, "with type:", element.1.dynamicType)
/*
key: content value: [1, 2, 3, 4] with type: Array<UInt8>
key: type value: test with type: String
*/
}

Related

Access to key inside Dictionary get in Swift

Is it possible to access the key inside of a Dictionary get in Swift ?
The main idea is:
In this code
var _dict:[String:String] = [:]
var dict:[String:String] {
//get the key
return _dict
}
_dict = ["key 1":"Value 1","key 2":"Value 2"]
print(dict["key 1"])
Access the key to check if the value exists, if it exists return the value if not generate the value for that key
Did you know that Dictionary allows you to specify a default value in its subscript to avoid dealing with optional values. It works like so:
let dict = ["a": 1, "b": 2]
let c = dict["c", default: 3]
print(c) // 3
but that doesn't change the dictionary - it's still only has "a" and "b" keys, which is the expected behavior.
I think what you're asking about is whether it's possible to mutate the dictionary with a default value. The answer is yes - you could create a subscript with a mutating get.
But it's the wrong thing to do!
You will effectively have a getter with side-effects, which is typically a bad practice.
In any case, this is how you could implement a subscript with a new parameter setDefault:
extension Dictionary {
subscript(key: Key, setDefault defaultVal: #autoclosure () -> Value) -> Value {
mutating get {
if let val = self[key] {
return val
} else {
let val = defaultVal()
self[key] = val
return val
}
}
}
}
// dict needs to be a var now
var dict = ["a": 1, "b": 2]
let c = dict["c", setDefault: 3]
Now, this will mutate dict and it will be ["a": 1, "b": 2, "c": 3]

getting unique items in an array [duplicate]

This question already has answers here:
Unique values of array in swift [duplicate]
(4 answers)
Removing duplicate elements from an array in Swift
(49 answers)
Closed 4 years ago.
I have an array which has repeated items, i want to get unique items only so i did this:
let decoded = userDefaults.object(forKey: "shifts") as! Data
myShifts = NSKeyedUnarchiver.unarchiveObject(with: decoded) as! [Shift]
filtered_shifts = myShifts.filter{$0.regionName == region && $0.cityName == city && $0.idService == idservice && $0.quantityStaff != 0}
for shift in filtered_shifts {
let decoded4 = userDefaults.object(forKey: "nationalities") as! Data
let decodedNationalities = NSKeyedUnarchiver.unarchiveObject(with: decoded4) as! [Nationality]
for nationality in decodedNationalities {
if nationality.id == shift.idNationality{
nationalitiesid.append(nationality.id)
nationalities.append(nationality.name)
}
}
}
uniqueNationality = Array(Set(nationalities))
uniqueNationalityid = Array(Set(nationalitiesid))
which is getting me a new array with unique items as i need...
the problem is that the order is diferent when i want the same order ... for exampple:
nationalitiesid = [3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4]
nationalities = ["Indian", "Indian", "Indian", "Indian", "Indian", "Indian", "Indian", "Indian", "Philippines", "Philippines", "Philippines", "Philippines"]
the unique array is:
uniqueNationality = ["Indian", "Philippines"]
uniqueNationalityid = [4, 3]
and this is wrong since Indian id is 3 and Philippines id is 4!
it should be:
uniqueNationalityid = [3, 4]
How to solve this?
let uniqueList = nationalities.reduce([], {
$0.contains($1) ? $0 : $0 + [$1]
})
This does not directly answer the question. This is an alternative solution. Using a model to group the related information rather keeping two separate arrays in sync.
For more information on Structs and Classes check the documentation
struct Nationality {
var id: Int
var name: String
}
let nationalities = [
Nationality(id: 3, name: "Indian"),
Nationality(id: 4, name: "Philippines")
]
let randomNationalityIds = [3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4]
let nationalitiesResult = Set(randomNationalityIds).flatMap { id in
nationalities.filter({ id == $0.id }).first
}
print(nationalitiesResult)
Output: [__lldb_expr_136.Nationality(id: 4, name: "Philippines"), __lldb_expr_136.Nationality(id: 3, name: "Indian")]
You don't need to keep these in order because the id and name are in the same place.
Instead of creating a non-unique array first and reducing it to a unique array afterwards you could as well collect the nationality strings in a Set of Nationalities:
var nationalitySet = Set<String>()
...
for nationality in decodedNationalities {
nationalitySet.append(nationality)
}
A set won’t store any duplicates so if you add a nationality which is already contained it won’t be added again.
As the set is unordered but converting it into a sorted array is an easy one:
let nationalities = nationalitiesSet.sorted { $0.name < $1.name }
This solution does, however, require Nationality to be hashable, i. e. it must conform to the Hashable protocol:
struct Nationality: Hashable {
var name: String
var id: Int
}

IOS - How to catch dynamic array dimension from JSON in Swift 3

Hello everyone I need help. I have JSON with dynamic array dimension value. This is the example :
//array 1 dimension
"array_dimension" : 1, //we can change to 2 dimension or more
"my_array" : [0, 1, 2, 3, 4] //the value adjust with array_dimension
//array 2 dimension
"array_dimension" : 2,
"my_array" : [[0, 1, 2], [3, 4], [5]]
//array 3 dimension
"array_dimension" : 3,
"my_array" : [[[0, 1], [2]], [[3], [4]], [[5]]]
And now I only can catch the my_array value in variable with static array dimension like this :
//example value in array 2 dimension
"array_dimension" : 2,
"my_array" : [[0, 1, 2], [3, 4], [5]]
//Program to catch my_array value in Swift 3
if let my_array = myJSON["my_array"] as? [[Int]] {
var myArray: [[Int]]?
myArray = my_array
} else {
print("\(TAG) error : JSON parsing my_array not found")
}
How can I catch all dynamic array dimension from my_array value in a variable (Swift 3) ?
UPDATE :
How I can use that technique ?
Your array is not Array<Int>, your array is Array<Any>.
Inside application you know how deep arrays in your root array. Based on that, you can take "last" meta array, that contains Int values (with type Array<Any>). After that you can get element by index and use:
let value : Int = Null;
if let intElement = array[index] as? Int {
value = intElement;
}
else
{
print("Error with element type (print element)");
}

How to convert dictionary to array

I want to convert my dictionary to an array, by showing each [String : Int] of the dictionary as a string in the array.
For example:
    
var myDict: [String : Int] = ["attack" : 1, "defend" : 5, "block" : 12]
    
I'm aware of myDict.keys.array and myDict.values.array, but I want them to show up in an array together. Here's what I mean:
    
var myDictConvertedToArray = ["attack 1", "defend 5", "block 12"]
You can use a for loop to iterate through the dictionary key/value pairs to construct your array:
var myDict: [String : Int] = ["attack" : 1, "defend" : 5, "block" : 12]
var arr = [String]()
for (key, value) in myDict {
arr.append("\(key) \(value)")
}
Note: Dictionaries are unordered, so the order of your array might not be what you expect.
In Swift 2 and later, this also can be done with map:
let arr = myDict.map { "\($0) \($1)" }
This can also be written as:
let arr = myDict.map { "\($0.key) \($0.value)" }
which is clearer if not as short.
The general case for creating an array out of ONLY VALUES of a dictionary in Swift 3 is (I assume it also works in older versions of swift):
let arrayFromDic = Array(dic.values.map{ $0 })
Example:
let dic = ["1":"a", "2":"b","3":"c"]
let ps = Array(dic.values.map{ $0 })
print("\(ps)")
for p in ps {
print("\(p)")
}
If you like concise code and prefer a functional approach, you can use the map method executed on the keys collection:
let array = Array(myDict.keys.map { "\($0) \(myDict[$0]!)" })
or, as suggested by #vacawama:
let array = myDict.keys.array.map { "\($0) \(myDict[$0]!)" }
which is functionally equivalent
With Swift 5
var myDict:[String : Int] = ["attack" : 1, "defend" : 5, "block" : 12]
let arrayValues = myDict.values.map({$0})
let arrayKeys = myDict.keys.map({$0})
You will have to go through and construct a new array yourself from the keys and the values.
Have a look at 's swift array documentation:
You can add a new item to the end of an array by calling the array’s
append(_:) method:
Try this:
var myDict:[String : Int] = ["attack" : 1, "defend" : 5, "block" : 12]
var dictArray: [String] = []
for (k, v) in myDict {
dictArray.append("\(k) \(v)")
}
Have a look at What's the cleanest way of applying map() to a dictionary in Swift? if you're using Swift 2.0:

Create an Array in Swift from an NSData Object

I'm trying to store an array of integers to disk in swift. I can get them into an NSData object to store, but getting them back out into an array is difficult. I can get a raw COpaquePointer to the data with data.bytes but can't find a way to initialize a new swift array with that pointer. Does anyone know how to do it?
import Foundation
var arr : UInt32[] = [32,4,123,4,5,2];
let data = NSData(bytes: arr, length: arr.count * sizeof(UInt32))
println(data) //data looks good in the inspector
// now get it back into an array?
You can use the getBytes method of NSData:
// the number of elements:
let count = data.length / sizeof(UInt32)
// create array of appropriate length:
var array = [UInt32](count: count, repeatedValue: 0)
// copy bytes into array
data.getBytes(&array, length:count * sizeof(UInt32))
print(array)
// Output: [32, 4, 123, 4, 5, 2]
Update for Swift 3 (Xcode 8): Swift 3 has a new type struct Data
which is a wrapper for NS(Mutable)Data with proper value semantics.
The accessor methods are slightly different.
Array to Data:
var arr: [UInt32] = [32, 4, UInt32.max]
let data = Data(buffer: UnsafeBufferPointer(start: &arr, count: arr.count))
print(data) // <20000000 04000000 ffffffff>
Data to Array:
let arr2 = data.withUnsafeBytes {
Array(UnsafeBufferPointer<UInt32>(start: $0, count: data.count/MemoryLayout<UInt32>.stride))
}
print(arr2) // [32, 4, 4294967295]
Update for Swift 5:
Array to Data:
let arr: [UInt32] = [32, 4, UInt32.max]
let data = Data(buffer: UnsafeBufferPointer(start: arr, count: arr.count))
print(data) // <20000000 04000000 ffffffff>
Data to Array:
var arr2 = Array<UInt32>(repeating: 0, count: data.count/MemoryLayout<UInt32>.stride)
_ = arr2.withUnsafeMutableBytes { data.copyBytes(to: $0) }
print(arr2) // [32, 4, 4294967295]
It's also possible to do this using an UnsafeBufferPointer, which is essentially an "array pointer", as it implements the Sequence protocol:
let data = NSData(/* ... */)
// Have to cast the pointer to the right size
let pointer = UnsafePointer<UInt32>(data.bytes)
let count = data.length / 4
// Get our buffer pointer and make an array out of it
let buffer = UnsafeBufferPointer<UInt32>(start:pointer, count:count)
let array = [UInt32](buffer)
This eliminates the need for initializing an empty array with duplicated elements first, to then overwrite it, although I have no idea if it's any faster. As it uses the Sequence protocol this implies iteration rather than fast memory copy, though I don't know if it's optimized when passed a buffer pointer. Then again, I'm not sure how fast the "create an empty array with X identical elements" initializer is either.
Here is a generic way to do it.
import Foundation
extension Data {
func elements <T> () -> [T] {
return withUnsafeBytes {
Array(UnsafeBufferPointer<T>(start: $0, count: count/MemoryLayout<T>.size))
}
}
}
let array = [1, 2, 3]
let data = Data(buffer: UnsafeBufferPointer(start: array, count: array.count))
let array2: [Int] = data.elements()
array == array2
// IN THE PLAYGROUND, THIS SHOWS AS TRUE
You must specify the type in the array2 line. Otherwise, the compiler cannot guess.
If you are dealing with Data to Array (I know for sure my array is going to be [String]), I am quite happy with this:
NSKeyedUnarchiver.unarchiveObject(with: yourData)
I hope it helps

Resources