Check whethere an array contain element - swift

I wonder why following not working:
var tmp = [AnyObject] ()
for (key, value) in dictionary{
if (tmp.contains(value)){
}
tmp.append(value as AnyObject)
}
Look like there is no method .contain for an Array. it force me to use some other method tmp.contains(where: <#T##(AnyObject) throws -> Bool#>)
But i dont want to. I simply want to check whether an array contain that specific object!

I recommend switching from AnyObject to AnyHashable(of course if that's possible), because AnyHashable is Equatable. Your code would look like this:
var tmp = [AnyHashable]()
var dictionary: [Int: Any] = [1: "Hello1", 2: "Hello2", 3: "Hello3", 4: false, 5: 289 ]
for (key, value) in dictionary{
if tmp.contains(value as! AnyHashable){
continue
}
tmp.append(value as! AnyHashable)
}
Hope it helps!

That's because AnyObject is not an Equatable object. If you have an AnyObject array, you have to do the comparisons yourself. If you used an array of Strings or a custom class for instance you could use this contains() method of Sequence

Related

How to add another dictionary entry to an existing dictionary to form a new dictionary (i.e. not append)

I want to add another dictionary entry to a dictionary in swift e.g.
let a: [String: Any] = ["Test": 1, "good":false]
let b = a + ["hello": "there"]
print(b)
(Sorry if + looks crazy here, as that's how Kotlin achieves this. I'm more familiar with Kotlin than Swift.)
But I get the error Binary operator '+' cannot be applied to two '[String : Any]' operands
I can't use updateValue too
let a: [String: Any] = ["Test": 1, "good":false]
let b = a.updateValue("hello": "there")
print(b)
It will error stating Cannot use mutating member on immutable value: 'a' is a 'let' constant
I can do it by an extension function as per proposed https://stackoverflow.com/a/26728685/3286489
but it looks overkill.
let b = a.merge(dict: ["hello": "there"])
print(b)
extension Dictionary {
func merge(dict: Dictionary<Key,Value>) -> Dictionary<Key,Value> {
var mutableCopy = self
for (key, value) in dict {
// If both dictionaries have a value for same key, the value of the other dictionary is used.
mutableCopy[key] = value
}
return mutableCopy
}
}
Is there a simple operator I can just add another entry to it to for a new dictionary?
Note: I'm not referring to append as per How to append elements into a dictionary in Swift?, as I receive an immutable dictionary let that I need to add another entry to it.
There is a built-in merging(_:uniquingKeysWith:) function on Dictionary that does exactly what you need.
let dictionary = [1:1]
let otherDictionary = [1:2, 2:3]
// This will take the values from `otherDictionary` if the same key exists in both
// You can use `$0` if you want to take the value from `dictionary` instead
let mergedDict = dictionary.merging(otherDictionary, uniquingKeysWith: { $1 })
If you want, you can easily define a + operator for Dictionary that uses the above function.
extension Dictionary {
static func + (lhs: Self, rhs: Self) -> Self {
lhs.merging(rhs, uniquingKeysWith: { $1 })
}
}
let addedDict = dictionary + otherDictionary

Swift: Dictionary of Dicitionaries, cannot get subscript

I've looked at other subscript issues here and I don't think they match my problem. I have a dictionary of dictionaries - Dictionary[String:Dictionary[String:String]]
In an extension I want to loop through all the values (Dictionary[String:String] and retrieve one of the values.
So I wrote this:
for dictNEO in Array(self.values) {
print(dictNEO)
print(type(of: dictNEO))
print(dictNEO["approachDate"])
}
and am getting this error on the last print line: Value of type 'Value' has no subscripts
Here's the first two print lines:
["nominalDist": "\"13.58 ", "approachDate": "\"2020-Feb-01 08:18 ± < 00:01\"", "minimumDist": "\"13.58 ", "diameter": "\"92 m - 210 m\"", "name": "\"(2017 AE5)\""]
Dictionary<String, String>
So I am confused as to why it is telling me it has no subscripts when it sees the type of as a Dictionary.
You have written this as an extension to Dictionary if I understand you correctly and that means that self is generic and defined as Dictionary<Key, Value> and not to you specific type so in your for loop you are looping over an array of [Value].
So you need to typecast Value before accessing it as a dictionary
if let dictionary = dictNEO as? [String: String] {
print(dictNEO["approachDate"])
}
but since it makes little sense to have an extension to Dictionary where you access a specific key it would be better to write it as a function. Since the dictionary is well defined now there is no issue with the last print
func printValuesForSubKey(_ key: String, _ dict: [String: [String: String]]) {
for (dictNEO) in dict.values {
print(dictNEO)
print(type(of: dictNEO))
print(dictNEO[key])
}
}
Note, I don't have an explanation why type(of:) recognises it as [String: String]
The code snippet doesn't work because values property is a collection of collections and with Array(values) you create a collection of collection of collections. In short, instead going down the code goes up and creates new collection level.
Solution with a Higher order function map:
self.values.map { print(type(of: $0)); $0["approachDate"] }
Solution with For-In Loop
for dictNEO in self.values {
print(dictNEO)
print(type(of: dictNEO))
print(dictNEO["approachDate"])
}

How to initialize a struct with dictionaries in Swift

I want to initialize every time a struct with dictionaries. Later, I'm going to use its properties instead a dictionary's keys and values - it seems rather easier. However, when I try the code below, it tells me that "Return from initializer without initializing all stored properties" and "1. 'self.one' not initialized" and "2. 'self.two' not initialized". My question is how to initialize a struct from a dictionary, so that I have basically a struct with the contents of the dictionary? Or how to transform it into struct?
struct Blabla {
var one: String
var two: [Int]
init(three: [String: [Int]]) {
for i in three {
self.one = i.key
self.two = i.value
}
} ERROR! - Return from initializer without initializing all stored properties
}
struct Blabla {
var one: String
var two: [Int]
init(three: [String: [Int]]) {
one = ""
two = []
for i in three {
self.one = i.key
self.two = i.value
}
} ERROR! - Return from initializer without initializing all stored properties
}
for in clause may have zero runs, in which case struct properties will not be initialized. You have to provide default values (or emit fatalError if you really need to).
While I think your example is pure synthetical, there is no need to loop through array, you can set properties to its last entry.
The issues is that if three is an empty Dictionary, the instance properties one and two don't get initialised. Also, you are overwriting the properties in each iteration of the for loop and the compiler cannot guarantee that there will be any iterations of the loop in compile-time, hence the compiler error.
You could make the initialiser failable to account for this by checking that the dictionary actually contains at least one key-value pair and assigning that first key-value pair to your properties.
struct Blabla {
var one: String
var two: [Int]
init?(three: [String: [Int]]) {
guard let key = three.keys.first, let value = three[key] else { return nil }
one = key
two = value
}
}
However, you should rethink what it is that you are actually trying to achieve, since with your current setup you have a mismatch between your init input values and the properties of your struct.
This code should compile, but it feels unsafe to me to initialize a Struct in this way because:
It assume your dictionary has values in it.
Your stored properties will always have the last value you looped through.
In order to pull values out to satisfy the compiler you need to force unwrap them. (With Dávid Pásztor's guard-letting approach, this can be avoided)
struct Blabla {
var one: String
var two: [Int]
init(three: [String: [Int]]) {
self.one = three.keys.first!
self.two = three[three.keys.first!]!
}
}
let input = ["pizza": [1,2]]
let l = Blabla(three: input)
If I were you I would let the memberwise initializer that you get for free do its thing and provide either a specialized initializer to handle your case of taking a Dictionary as input or move that parsing to another function/class/etc....
The compiler error is clear: If the dictionary is empty the struct members are never initialized. But the code makes no sense anyway as each iteration of the dictionary overwrites the values.
Maybe you mean to map the dictionary to an array of the struct
struct Blabla {
let one: String
let two: [Int]
}
let three = ["A":[1,2], "B":[3,4]]
let blabla = three.map{Blabla(one: $0.key, two: $0.value)}
print(blabla) // [Blabla(one: "A", two: [1, 2]), Blabla(one: "B", two: [3, 4])]
struct blabla{
var a : string
var b : [int] = []
init(_ data: [string:[int]]){
// whatever you want to do
}
}

Cast Array<Any> to Array<Int> in Swift

I have array of Any objects called timestamps, in which first object is string, and rest objects are Int. What i want is, to drop first element and treat rest of array as Array of Int objects..
I tried:
let timestamps = xAxisData.dropFirst()
if let timestamps = timestamps as? Array<Int> {
}
It compile, but it says - Cast from 'ArraySlice<Any>' to unrelated type 'Array<Int>' always fails
I could possibly iterate through array and create new temporary array with check every element whether it Int or not, but i think there is a better cleaner way?
You need to create an Array from the ArraySlice created by dropFirst.
let timestamps = Array(xAxisData.dropFirst())
if let timestamps = timestamps as? [Int] {
}
I like to use something like a flatMap so no matter were the non-Int values are in the array they are not consider for the resulting array.
let originalArray:[Any] = ["No int", 2, 3, "Also not int", 666, 423]
let intArray:[Int] = originalArray.flatMap { $0 as? Int }
print(intArray)
Will print this:
[2, 3, 666, 423]
So in your case you could do something like:
let timestamps = xAxisData.flatMap { $0 as? Int }
As stated by Steve Madsen in the comment below, if you are using Swift 4.1 or newer Apple suggest to used compactMap for getting rid of optional nils so that specific use of flatMap is deprecated.
let timestamps = xAxisData.compactMap { $0 as? Int }
Here is another method to convert a subset of an array with mixed types to something more specific:
let intArray = anyArray.compactMap { $0 as? Int }

Cannot cast CountablePartialRangeFrom<Int> to Range<Int>

I have a parameter for a method that is of type Range<Int>? that defaults to nil. I'd like a nice succinct way of saying "Use the given range or use the whole range". Nothing I've done seems to quite work, I feel like this should though...
class func fileNames(entries: Array<DBFILESMetadata>, range: Range<Int>? = nil) -> Array<String> {
return entries[range ?? 0... as Range<Int>].map{ $0.name }.filter{ $0.hasSuffix(".jpg") }
}
...I get the error Cannot convert value of type 'CountablePartialRangeFrom<Int>' to type 'Range<Int>' in coercion, which I totally get, but then why does this work (via pattern #6 here: https://stackoverflow.com/a/42860580/84783)
let array = ["a", "b", "c"]
let range = 1...
let arraySlice = array[range]
I can just do this in a conditional, but feel like a one liner should work.
You can just change your method parameter to CountableRange and pass the array indices property in case of nil:
class func fileNames(entries: Array<DBFILESMetadata>, range: CountableRange<Int>? = nil) -> Array<String> {
return entries[range ?? entries.indices].map{ $0.name }.filter{ $0.hasSuffix(".jpg") }
}