As I am new to swift programming language .I am using the dictionary of two items now i need to take the index path of particular dictionary value .I am using the following code
var dictionaryitems = ["computer":"something to make work easy","pen":"used for writing something"]
print(dictionaryitems["pen"])
Use firstIndex for this
let index = dictionaryitems.firstIndex(where: {$0.key == "pen"})
you can get index of key or value by
let index = Array(Dictionary.keys).index(of: key/value)
by this you will get an optional value which you can unwrap using if-let or guard statement for further use
var dictionaryitems = ["computer":"something to make work easy","pen":"used for writing something"]
if let index = dictionaryitems.index(forKey: "pen") {
print(dictionaryitems[index].key, ":", dictionaryitems[index].value)
}
This is an example how you can get the index of a dictionary using swift >
if let index = carDataArray?.index(where: {$0["carName"] as! String == "BMW"}) {
print("Car Found")
}
Related
I am looping trough a snapshot retrieved from a Firestore database getting values into my custom object with conditional unwrapping like the example below. It works fine as long as the key has a value, but as soon as it is empty i get an exception on line if let temp = document.get("windGust") as! String? (See error further down)
I thought conditional unwrapping was supposed to handle this ?
Can anyone point me in the right direction on how to handle this in code? If the value of windGust is empty it should just ignore it and continue.
db.collection("yrData").getDocuments { (snapshot, error) in
for document in snapshot!.documents {
let yrData = YrData()
if let temp = document.get("windGust") as! String?
{
yrData.windGust = temp
}
The error:
Could not cast value of type '_NSZeroData' (0x7fff87d0b5b8) to 'NSString' (0x7fff87d0eee8).
2020-01-16 21:29:23.417663+0100 Victoria[13603:708774] Could not cast value of type '_NSZeroData' (0x7fff87d0b5b8) to 'NSString' (0x7fff87d0eee8).
Maybe not an answer to my question, but this is a solution:
Instead of using:
if let temp = document.get("windGust") as! String?
{
yrData.windGust = temp
}
Use data() like this:
yrData.windGust = data["windGust"] as? String ?? ""
You may just need to use the null coalescing operator.
Something like that would read; if windGust lookup is not a string, assign an empty string.
yrData.windGust = document.get("windGust") as? String ?? ""
Just trying to remove the first character from a string in Swift. I use the code written below, but the second line keeps crashing my application.
Is this not the correct way to unwrap a String Index? What is?
var tempText = text
let toRemove = tempText?.startIndex ?? String.Index(0)
tempText?.remove(at: toRemove)
You can use Collection method dropFirst:
if let text = text { // you need also to unwrap your optional
let tempText = String(text.characters.dropFirst()) // And initialize a new String with your CharacterView
}
In Swift 4 String conforms to Collection so you can use it directly on your string:
if let text = text {
let tempText = text.dropFirst() // "bc"
}
You are initializing a String.Index type instead of getting the index of the tempText string.
Moreover, startIndex is not an optional, tempText, however, is.
You should check if tempText exists and is not empty (you can simply do this with an if let), and remove the character at startIndex if it matches those conditions.
var tempText = text
if let toRemove = tempText?.startIndex {
tempText?.remove(at: toRemove)
}
If you are using swift 4, you can use:
var tempText = text.dropFirst()
I am doing this to loop through my dictionary until until I match the key. My dictionary is defined as [Int:String]
var index = 0
for (key, value) in mylist! {
if key == property.propertyValue as! Int {
// use index here
}
index += 1
}
Is there a better way to do this? I see examples of filtering (something like the example below) but I am not sure how to make it work with a dictionary. Could I use something like this to find the index of the item? Or is there another way?
mylist.filter{$0.key == 1}
UPDATE:
This works:
let index = Array(mylist!.keys).index(of: 1)
But this doesn't:
let index = mylist!.index(forKey: 1)
It seems they both should work. I wonder why the 2nd one doesn't.
A dictionary is an unordered collection type and doesn't have an index.
You can get the value directly by the key
let value = mylist[property.propertyValue as! Int]
If I understand you correctly, you could do it like so:
let myList = [
2: "Hello",
4: "Goodbye",
8: "Whats up",
16: "Hey"
]
let index = Array(myList.keys).index(of: property.propertyValue)
And then to find the key you're looking for again...
let key = Array(myList.keys)[index!]
As said in other answers, a dictionary is probably not the data structure you're looking for. But this should answer the question you've asked.
Given your dictionary
let dict = [1:"a", 2:"b", 3: ""]
you can extract the index of a given key (e.g. `1) simply writing
let indexForKey1 = dict.index(forKey: 1)
Fetching all the indexes
You can also build a dictionary where they key is the index and the value is the key of dict
let indexes = dict.keys.map { dict.index(forKey: $0) }
BTW: what do you really need to do?
Ok, I am working in an iMessage app and am trying to parse more than 1 url query item from the selected message here- I have been successful getting/sending just 1 value in a query:
override func willBecomeActive(with conversation: MSConversation) {
// Called when the extension is about to move from the inactive to active state.
// This will happen when the extension is about to present UI.
if(conversation.selectedMessage?.url != nil) //trying to catch error
{
let components = URLComponents(string: (conversation.selectedMessage?.url?.query?.description)!)
//let val = conversation.selectedMessage?.url?.query?.description
if let queryItems = components?.queryItems {
// process the query items here...
let param1 = queryItems.filter({$0.name == "theirScore"}).first
print("***************=> GOT IT ",param1?.value)
}
}
When I just have 1 value, just by printing conversation.selectedMessage?.url?.query?.description I get an optional with that 1 value, which is good. But with multiple I cant find a clean way to get specific values by key.
What is the correct way to parse a URLQueryItem for given keys for iMessage?
When you do conversation.selectedMessage?.url?.query?.description it simply prints out the contents of the query. If you have multiple items then it would appear something like:
item=Item1&part=Part1&story=Story1
You can parse that one manually by splitting the string on "&" and then splitting the contents of the resulting array on "=" to get the individual key value pairs in to a dictionary. Then, you can directly refer to each value by key to get the specific values, something like this:
var dic = [String:String]()
if let txt = url?.query {
let arr = txt.components(separatedBy:"&")
for item in arr {
let arr2 = item.components(separatedBy:"=")
let key = arr2[0]
let val = arr2[1]
dic[key] = val
}
}
print(dic)
The above gives you an easy way to access the values by key. However, that is a bit more verbose. The way you provided in your code, using a filter on the queryItems array, is the more compact solution :) So you already have the easier/compact solution, but if this approach makes better sense to you personally, you can always go this route ...
Also, if the issue is that you have to write the same filtering code multiple times to get a value from the queryItems array, then you can always have a helper method which takes two parameters, the queryItems array and a String parameter (the key) and returns an optional String value (the value matching the key) along the following lines:
func valueFrom(queryItems:[URLQueryItem], key:String) -> String? {
return queryItems.filter({$0.name == key}).first?.value
}
Then your above code would look like:
if let queryItems = components?.queryItems {
// process the query items here...
let param1 = valueFrom(queryItems:queryItems, key:"item")
print("***************=> GOT IT ", param1)
}
You can use iMessageDataKit library. It makes setting and getting data really easy and straightforward like:
let message: MSMessage = MSMessage()
message.md.set(value: 7, forKey: "user_id")
message.md.set(value: "john", forKey: "username")
message.md.set(values: ["joy", "smile"], forKey: "tags")
print(message.md.integer(forKey: "user_id")!)
print(message.md.string(forKey: "username")!)
print(message.md.values(forKey: "tags")!)
(Disclaimer: I'm the author of iMessageDataKit)
I want to retrieve a random emoji inside the range.
let emojiRanges = [
0x1F601...0x1F64F,
0x1F680...0x1F6C0,
]
let flattenEmoji = emojiRanges.flatten()
// the loop for emoji works
for i in flattenEmoji {
let st = String(format:"0x%2X %#", i, String(UnicodeScalar(i)))
print(st)
}
// but this is not possible to obtain value at wanted index
//there is a compiler error:
let randomSign = String(UnicodeScalar(flattenEmoji[arc4random_uniform(UInt32(flattenEmoji.count))]))
print("RANDOM \(randomSign)")
the error:
ViewController.swift:68:67: Cannot subscript a value of type
'FlattenBidirectionalCollection<[Range]>' (aka
'FlattenBidirectionalCollection>>') with an index of
type 'UInt32'
What is the proper way to get a result?
The problem is that flatten() is lazily applied, and therefore returns a special FlattenBidirectionalCollection, which is indexed by a FlattenBidirectionalCollectionIndex, rather than an Int.
The simplest solution therefore would be to simply use the Array(_:) constructor (or flatMap(_:)) in order to eagerly apply the flattening of the ranges, which will create an array that you can then subscript with an Int.
let flattenEmoji = Array(emojiRanges.flatten()) // In Swift 3, flatten() is named joined()
let randomIndex = Int(arc4random_uniform(UInt32(flattenEmoji.count)))
let randomSign = String(UnicodeScalar(flattenEmoji[randomIndex]))
If you wish to keep the flattening being lazily applied, you could subscript the FlattenBidirectionalCollection directly (for Swift 2) through using advancedBy(_:) on the collection's startIndex:
let randomIndex = flattenEmoji.startIndex.advancedBy(Int(arc4random_uniform(UInt32(flattenEmoji.count))))
let randomSign = String(UnicodeScalar(flattenEmoji[randomIndex]))
In Swift 3, as collections move their indices, you'd want use the collection's index(_:offsetBy:) method instead:
let randomIndex = flattenEmoji.index(flattenEmoji.startIndex, offsetBy: Int(arc4random_uniform(UInt32(flattenEmoji.count))))
Change emojiRanges declaration to this:
let emojiRanges = Array(0x1F601...0x1F64F) + Array(0x1F680...0x1F6C0)
then life will become much easier.
for i in emojiRanges {
let st = String(format:"0x%2X %#", i, String(UnicodeScalar(i)))
print(st)
}
in randomSign you should convert index to Int
let randomSign = String(UnicodeScalar(emojiRanges[Int(arc4random_uniform(UInt32(emojiRanges.count)))]))
print("RANDOM \(randomSign)")