Loop through array issue in swift - swift

I have an array in which there are two values coming. I want to get them out of an array and pass the value according to index base to var1 and var2. I am looping through the array but when run the app it does not come inside the for loop. I have used break points also but it does not come inside the loop.
Code I have tried,
let myarray = UserDefaults.standard.stringArray(forKey: "selectArray") ?? [String]()
for (index, value) in myarray.enumerated() {
print("\(index): \(value)")
if index == 0{
listItem = value
print(listItem)
}else
{
CuisineItem = value
print(CuisineItem)
}
}
How can I get the value out now in two separate variables?

How you set array in UserDefaults. Look here my code works well
var array1: [[String]] = [[String]]()
array1 = [["key1", "val2"],["key2", "val2"]]
UserDefaults.standard.set(array1, forKey: "selectArray")
let myarray = UserDefaults.standard.value(forKey: "selectArray") as? [[String]]
for (index, value) in (myarray?.enumerated())! {
for (index, value) in value.enumerated() {
print("\(index): \(value)")
if index == 0 {
listItem = value
print(listItem)
}else {
CuisineItem = value
print(CuisineItem)
}
}
}

As you have mentioned that your array is of 2-D so , you can try code given below :
for oneDArray in myarray {
for(index,value) in oneDArray.enumerated(){
print("\(index1): \(value1)")
if index == 0{
listItem = value
print(listItem)
}
else {
CuisineItem = value
print(CuisineItem)
}
}
}
It should be noted that myarray is two-dimensional array , and oneDArray is one-dimensional array.

You can easily save and retrieve multi dimensional array in user defaults. Try the below code in XCode Playground, it works like a charm.
import Foundation
let array = [["a", "b", "c"], ["a", "b", "c"]];
func testArray() {
UserDefaults.standard.setValue(array, forKey: "test");
guard let testArray = UserDefaults.standard.array(forKey: "test") as? [[String]] else {
return
};
print(testArray)
}
testArray()

Related

Swift - converting an array to struct

I have an array with a list of data as following Data = [City, ZipCode, gpsLat, gpsLong]
I am trying to convert in struct as follows
struct Location {
var zipCode: Double
var nameCity: String
var gpsLat: DOuble
var gpsLong: Double
}
I am using the for-in loop
var resultat :[Location] = []
for item in Data {
resultat.append(Location(zipCode: Data[item][0], City : Data[item][1], gpsLat : Data[item][2], gpsLong : Data[item][3]))
}
I got the message
Cannot convert value of type '[String]' to expected argument type 'Int'
As #Achu says, you should use item[0], not `Data[item][0]
A for ... in loop defines a new local variable that contains each element in your array.
So if you have an array:
let array = ["now", "is", "the", "time"]
Normally you would loop through the array like this:
for item in array {
print(item)
}
That is the recommended way to do it.
If you wanted to loop through the indexes, that code would look like this:
for index in array.indices {
let item = array[index]
print(item)
}
or this:
for index in 0..<array.count {
let item = array[index]
print(item)
}
Note that a third option would be to use the Array object's enumerated() method:
for (index, item) in array.enumerated() {
print("item at index \(index) = '\(item)'")
}
That would output
item at index 0 = 'now'
item at index 1 = 'is'
item at index 2 = 'the'
item at index 3 = 'time'
You could also write that loop as a forEach:
array.enumerated().forEach() { (index, item) in
print("item at index \(index) = '\(item)'")
}
All that said, your use-case doesn't really lend itself to loops. If your array only contains a single set of Location properties, you probably want:
if let zipCode = Data[0] as Double,
let nameCity = Data[1] as? String,
let gpsLat = Data[2] as? Double,
let gpsLong = Data[3] as? Double {
let location = Location(zipCode: zipCode,
nameCity: nameCity,
gpsLat: gpsLat,
gpsLong: gpsLong)
// Do something with the location we just created
}
(BTW, a Double is a bad way to store zipcodes. I would suggest using Ints or Strings. Ints work well unless you have a zip code with a leading zero, in which case the leading zero would be truncated.)

Filter an (Codable) array by another array

I'm trying to filter my json data by IDs (trying mark some favourites and filter using it)
struct workoutList : Codable {
let id : Int
let title : String
let tag : String
}
func selectedWorkoutGroup(libraryFilter: Int, jsonErgWorkouts:[workoutList], workoutGroupBox: UITextField) -> [workoutList] {
var selectedGroup = [workoutList]()
let workoutFav = [1,10,100]
if libraryFilter == 0 {
// This works because I'm filtering based on 1 specific item
selectedGroup = jsonErgWorkouts.filter { $0.tag == workoutGroupBox.text }
} else if libraryFilter == 1 {
// Here I want to filter and show only the favorites
selectedGroup = jsonErgWorkouts.filter { $0.id } //
print("selectedGroup:\(selectedGroup)")
}
return selectedGroup
}
in the above code, the filter works when I have 1(one) something specific item to filter and then I get the entire json array with that tag.
Now I want to implement a favorite list, where the user selects for example ID == [1, 10 ,100] as their favourite.
How can I use the filter command to do it? I tried a few things and searched through SO (but doesn't work). Most of the answers are based on filtering based on specific items eg:
selectedGroup = jsonErgWorkouts.filter { workoutFav?.contains($0.id) }
edit: (omitted that I am using/storing the favourites in userDefaults. This code gives the error of "type of expression is ambiguous without more context"
func selectedWorkoutGroup(libraryFilter: Int, jsonErgWorkouts:[workoutList], workoutGroupBox: UITextField) -> [workoutList] {
var selectedGroup = [workoutList]()
UserDefaults.standard.set([1,10,100], forKey: "workoutFavorite")
/// This one gets stored as [Any] so I cast it to [Int]
let workoutFav = UserDefaults.standard.array(forKey: "workoutFavorite") as? [Int]
if libraryFilter == 0 {
// This works because I'm filtering based on 1 specific item
selectedGroup = jsonErgWorkouts.filter { $0.tag == workoutGroupBox.text }
} else if libraryFilter == 1 {
selectedGroup = workoutFav.flatMap { favouriteId in // for each favourite ID
jsonErgWorkouts.filter { $0.id == favouriteId } // This returns Error "type of expression is ambiguous without more context"
} // flatMap joins all those arrays returns by "filter" together, no need to do anything else
print("selectedGroup:\(selectedGroup)")
}
return selectedGroup
}
Final Solution:
Changing from This
let workoutFav = UserDefaults.standard.array(forKey: "workoutFavorite") as? [Int]
to This (notice the as! instead of as?)
let workoutFav = UserDefaults.standard.array(forKey: "workoutFavorite") as! [Int]
works using #sweeper's answer. Thanks
Update:
Figured out why this error occurred "type of expression is ambiguous without more context" when casting the output of UserDefaults as? [Int] and had to use as! [Int]
But using as! [Int] force unwrapping it causes app to crash if the user did not have any favorites saved into the UserDefault. (Which I then had to code around) like below
var workoutFav = [Int]()
if !(UserDefaults.standard.array(forKey: "workoutFavorite") == nil) {
workoutFav = UserDefaults.standard.array(forKey: "workoutFavorite") as! [Int]
}
Which was then simplified and removed the force unwrapping based on this SO https://stackoverflow.com/a/37357869/14414215 to become this one-line
let workoutFav = UserDefaults.standard.array(forKey: "workoutFavorite") as? [Int] ?? [Int]()
You need to do that filter for each id in the favourites array. You get an array of arrays as a result. To get the final array, you need to join those arrays to a single array. This "map each thing to an array and join the arrays" operation is what a flatMap does:
workoutFav.flatMap { favouriteId in // for each favourite ID
jsonErgWorkouts.filter { $0.id == favouriteId } // find workouts that match the ID
} // flatMap joins all those arrays returns by "filter" together, no need to do anything else
First thing first please give a struct name with a capital so you can distinguish between instance of it. Second you need to have new array where you will store each favorite, and store permanently that array, core data or some base on server, form there you will fetch favorites.
The better way is to add property like isFavorite: Bool that is false by default, and if user change it you can set it to be true, in that way you can avoid using ids for that and you can store whole workout's in one array to core data or base that you use, after that you can fetch from there with
let favorites = workouts.compactMap { $0.isFavorite == true }
Here you go in that way, but just to mention it highly recommended that you store those type of data outside User defaults.
struct Fav {
let name: String
let id: String
}
let df = UserDefaults.standard
let jk = ["aaa", "bbb", "cccc"]
df.setValue(jk, forKey: "favorites")
let fav1 = Fav(name: "zzz", id: "aaa")
let fav2 = Fav(name: "bbb", id: "qqq")
let favs = [fav1, fav2]
let favIDs = df.value(forKey: "favorites") as? [String]
favIDs?.forEach({ (id) in
let f = favs.filter({$0.id == id}) // here it is
})

Random Elements from Dictionary

Here is my Array of Dictionary,
var myArrayOfDict = [["vegetables": ["CARROT","BEANS"], "fruits": ["APPLE","MANGO","BANANA"], "letters":["A","B","C","D"],"numbers":["ONE","TWO","THREE"],"shapes":["SQUARE","RECTANGLE","CIRCLE"]]]
How do i get the desired output, actually i need to get random selected elements of the specified range ...(i.e) when i need 3 elements randomnly from dictionary as like,
[["fruits": ["APPLE","MANGO","BANANA"],"shapes":["SQUARE","RECTANGLE","CIRCLE"],"numbers":["ONE","TWO","THREE"]]]
When i need just 2 elements randomnly like,
[["shapes":["SQUARE","RECTANGLE","CIRCLE"],"fruits": ["APPLE","MANGO","BANANA"]]]
Thanks in Advance,
Here is one solution using randomElement().
func randomSelection(from dict: [String: [String]], count: Int) -> [String: [String]] {
guard !dict.isEmpty else { return [:] }
var result = [String: [String]]()
for i in 0..<count {
let element = dict.randomElement()! //We know dictionary is not empty
result[element.key] = element.value
}
return result
}
The above solution might return less elements in a dictionary than expected if the same element is returned more than once from randomElemnt(). If this should be voided the below solution should work
func randomSelection(from dict: [String: [String]], count: Int) -> [String: [String]] {
guard !dict.isEmpty else { return [:] }
guard dict.count > count else { return dict }
var result = [String: [String]]()
while result.count < count {
let element = dict.randomElement()!
if result[element.key] == nil {
result[element.key] = element.value
}
}
return result
}
Since the function takes a dictionary as the first argument the array needs to be looped over
for d in myArrayOfDict {
print(randomSelection(from: d, count: 2))
}
Array myArrayOfDict contains a single Dictionary. So, it doesn't make sense getting a random element from it.
As your example explains, you need to get random elements from the Dictionary itself.
So, you can use randomElement to get that working.
let myArrayOfDict = ["vegetables": ["CARROT","BEANS"], "fruits": ["APPLE","MANGO","BANANA"], "letters":["A","B","C","D"],"numbers":["ONE","TWO","THREE"],"shapes":["SQUARE","RECTANGLE","CIRCLE"]]
var elements = [String : [String]]()
let count = 2
for _ in 0..<count {
if let element = myArrayOfDict.randomElement() {
elements[element.key] = element.value
}
}
print(elements)

swift array optional to string

I'm new to Swift and find out that Swift has optional string. I have issue unwrapping this optional. Here's the exmple:
for row in try database.prepare("SELECT name FROM airline WHERE carrier_id = \"\(text2)\"") {
print(row)
}
Results:
[Optional("Lion Air")]
[Optional("Malindo Air")]
I tried:
if let a = row {
print(a)
}
but it shows the error:
'Statement.Element' (aka 'Array<Optional<Binding>>')
How can I unwrap that array string and just leave as string as usual?
try these and see:
// According to your current result
for arrayElement in row {
print(arrayElement)
if let arrayString = arrayElement.first {
print(arrayString)
}
}
Try this for proper solution:
for childArray in row {
print("childArray - \(childArray)")
for stringValue in childArray {
print("stringValue - \(stringValue)")
}
}
Here is tested solution
let row = [
[Optional("Lion Air")],
[Optional("Malindo Air")]
]
row.forEach { (childArray) in
childArray.forEach({ (optinalString) in
print("optinalString - \(String(describing: optinalString))")
if let unoptionalString = optinalString {
print("unoptionalString - \(unoptionalString)")
}
})
}
Result:
Try this, using flatMap
Here is example optional string array to string array using flatMap
let optarray = [Optional("Swift"),Optional("Java"),Optional("C"), nil]
let stringArray = optarray.flatMap{$0}
print(stringArray)
Output
["Swift", "Java", "C"]

Swiftier Swift for 'add to array, or create if not there...'

I've noticed a common pattern in Swift is
var x:[String:[Thing]] = [:]
so, when you want to "add an item to one of the arrays", you can not just
x[which].append(t)
you have to
if x.index(forKey: which) == nil {
x[which] = []
}
x[which]!.append(s!)
Really, is there a swiftier way to say something like
x[index?!?!].append??(s?!)
While this is a question about style, performance seems to be a critical issue when touching arrays in Swift, due to the copy-wise nature of Swift.
(Please note, obviously you can use an extension for this; it's a question about Swiftiness.)
Swift 4 update:
As of Swift 4, dictionaries have a subscript(_:default:) method, so that
dict[key, default: []].append(newElement)
appends to the already present array, or to an empty array. Example:
var dict: [String: [Int]] = [:]
print(dict["foo"]) // nil
dict["foo", default: []].append(1)
print(dict["foo"]) // Optional([1])
dict["foo", default: []].append(2)
print(dict["foo"]) // Optional([1, 2])
As of Swift 4.1 (currently in beta) this is also fast,
compare Hamish's comment here.
Previous answer for Swift <= 3: There is – as far as I know – no way to "create or update" a dictionary
value with a single subscript call.
In addition to what you wrote, you can use the nil-coalescing operator
dict[key] = (dict[key] ?? []) + [elem]
or optional chaining (which returns nil if the append operation
could not be performed):
if dict[key]?.append(elem) == nil {
dict[key] = [elem]
}
As mentioned in SE-0154 Provide Custom Collections for Dictionary Keys and Values and also by #Hamish in the comments, both methods
make a copy of the array.
With the implementation of SE-0154 you will be able to mutate
a dictionary value without making a copy:
if let i = dict.index(forKey: key) {
dict.values[i].append(elem)
} else {
dict[key] = [key]
}
At present, the most efficient solution is given by Rob Napier
in Dictionary in Swift with Mutable Array as value is performing very slow? How to optimize or construct properly?:
var array = dict.removeValue(forKey: key) ?? []
array.append(elem)
dict[key] = array
A simple benchmark confirms that "Rob's method" is the fastest:
let numKeys = 1000
let numElements = 1000
do {
var dict: [Int: [Int]] = [:]
let start = Date()
for key in 1...numKeys {
for elem in 1...numElements {
if dict.index(forKey: key) == nil {
dict[key] = []
}
dict[key]!.append(elem)
}
}
let end = Date()
print("Your method:", end.timeIntervalSince(start))
}
do {
var dict: [Int: [Int]] = [:]
let start = Date()
for key in 1...numKeys {
for elem in 1...numElements {
dict[key] = (dict[key] ?? []) + [elem]
}
}
let end = Date()
print("Nil coalescing:", end.timeIntervalSince(start))
}
do {
var dict: [Int: [Int]] = [:]
let start = Date()
for key in 1...numKeys {
for elem in 1...numElements {
if dict[key]?.append(elem) == nil {
dict[key] = [elem]
}
}
}
let end = Date()
print("Optional chaining", end.timeIntervalSince(start))
}
do {
var dict: [Int: [Int]] = [:]
let start = Date()
for key in 1...numKeys {
for elem in 1...numElements {
var array = dict.removeValue(forKey: key) ?? []
array.append(elem)
dict[key] = array
}
}
let end = Date()
print("Remove and add:", end.timeIntervalSince(start))
}
Results (on a 1.2 GHz Intel Core m5 MacBook) for 1000 keys/1000 elements:
Your method: 0.470084965229034
Nil coalescing: 0.460215032100677
Optional chaining 0.397282958030701
Remove and add: 0.160293996334076
And for 1000 keys/10,000 elements:
Your method: 14.6810429692268
Nil coalescing: 15.1537700295448
Optional chaining 14.4717089533806
Remove and add: 1.54668599367142