Swift how to remove elements from an array of enums? - swift

I have an array of enums and I'd like to remove x number of elements from it, the code below is what I'm trying to achieve, it partially works because it removes elements only from the more variable created in the switch-case but the original array doesn't change
MyArray of enums
Contacts
More
if more is present it means there are more contacts to download, when the user tap on a button it should remove ids that has been downloaded
Here is an example:
// Switch last element of my array
switch model.myArray[model.myArray.count-1] {
// If last element is More type
case var more as More:
// Download new contacts
downloadfunction()
// Remove 100 contacts that has been downloaded
let range = 0...99
more.peopleIds?.removeSubrange(range)
}
More structure
public struct More: Decodable, Identifiable {
public let id: String?
public var peopleIds: [String]?

I think the best way to check the type of the last element of the array is to cast it using an if var ...
if var more = model.myArray.last as? More {
and then change it and replace the old value in the array
if var more = myArray.last as? More, let array = more.peopleIds {
more.peopleIds = Array(array.dropFirst(100))
myArray[myArray.endIndex - 1] = more
}

Related

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
}
}

Value of type _ cannot convert to specified type Set<> Swift

I'm trying to create a set of random exercises. I have made my struct Hashable and Equatable following the tutorial here https://medium.com/#JoyceMatos/hashable-protocols-in-swift-baf0cabeaebd and that is working fine so it's ready to be put in a Set<>.
When I use an Array to collect the workout exercises, as per below, it works fine. But when I switch to a Set<> I get an error "cannot convert value of type [_] to specified type 'Set'. What is it about 'Sets' that mean you can't map in the same way as an Array?
func generateWorkout() {
let allPossibleExercises = masterExerciseArray
let numberOfExercisesKey = Int(arc4random_uniform(4)+3)
//error triggers on the line below if I switch [WorkoutExercise]
//for Set<WorkoutExercise> (which conforms to Hashable/Equatable
let workoutSet : [WorkoutExercise] = (1...numberOfExercisesKey).map { _ in
let randomKey = Int(arc4random_uniform(UInt32(allPossibleExercises.count)))
return WorkoutExerciseGenerator( name: allPossibleExercises[randomKey].name,
maxReps: allPossibleExercises[randomKey].maxReps).generate()
}
print (workoutSet)
}
There is an answer here with a similar error message Cannot convert value of type '[_]' to specified type 'Array' but my array wouldn't be empty as in this example so I don't think this is the same root cause?
UPDATE : for anyone having the same problem, you can use Array but then simply convert the Array to a Set afterwards if the correct elements are Hashable/Equatable
If creating the array works create the array and then make the Set from the array. If all involved objects conform to Hashable this is supposed to work.
func generateWorkout() {
let allPossibleExercises = masterExerciseArray
let numberOfExercisesKey = Int(arc4random_uniform(4)+3)
let workoutArray : [WorkoutExercise] = (1...numberOfExercisesKey).map { _ in
let randomKey = Int(arc4random_uniform(UInt32(allPossibleExercises.count)))
return WorkoutExerciseGenerator( name: allPossibleExercises[randomKey].name,
maxReps: allPossibleExercises[randomKey].maxReps).generate()
}
let workoutSet = Set(workoutArray)
print (workoutSet)
}

Delete specific object from LinkingObjects list - Realm Swift

I am currently trying out Realm on a test project and I have been struggling with removing a specific object from a List. LensDBObject and ListDBObject. LensDBObject contains a list of lenses and ListDBObject are lists of existing lenses. A lens can be in multiple lists and I'd like to remove a specific lens from a specific list but not remove if from the other lists.
Below are my two classes:
#objcMembers class LensDBObject: Object {
dynamic var id = UUID().uuidString
dynamic var manufacturer = ""
dynamic var series = ""
dynamic var serial = ""
dynamic var isSelected = false
override static func primaryKey() -> String? {
return "id"
}
let objects = LinkingObjects(fromType: ListDBObject.self, property: "lensList")
}
#objcMembers class ListDBObject: Object {
dynamic var listName = ""
let lensList = List<LensDBObject>()
}
Below is my code to find a specific lens in the list I want. The values returned are what I expect.
let listToSearch = realm.objects(ListDBObject.self).filter("listName == %#", "List 542")
print(listToSearch)
let filteredResults = listToSearch[0].lensList.filter("manufacturer == %# AND series == %# AND serial == %#", "Panavision" , "Primo Prime", "407")
print(filteredResults)
However, when I try to delete filteredResults, it deletes it from the lensDBOject altogether. I just want to be able to delete this specific lens from this specific list.
try! realm.write {
realm.delete(filteredResults)
}
I tried using for loops to get the index of the lens in the list and then delete it directly from that. But it still deletes the lens everywhere.
Am I missing something? Should I be using a one-to-many relationship as opposed to a LinkingObject?
Thanks for you help!
Try something like this. You only want to remove the lens from the list, not delete it from the Realm.
try! realm.write {
filteredResults.forEach { lens in
if let index = listToSearch[0].lensList.index(of: lens) {
listToSearch[0].lensList.remove(at: index)
}
}
}
Note that this will remove from that one specific list all lenses that match your filter.
Edit: Updated to reflect Realm's custom List class.
The if let is required, because index(of:) could potentially return nil if the object is not found in the list. Additionally, we must do it one item at a time rather than getting all the indexes first, since removing an item would cause the index array to be wrong.

From Dictionary to Array and back

So, a little background info:
I use a Dictionary to store info from wich a collectionview is populated and the info in the dictionary is paired with data from a urlsession.
My Dictionary looks like this:
var allDict = ["Section1": ["device1", "device2"], "Section2": ["device3", "device4"]]
I then use a struct to get an array which feeds my collectionview cells and sections:
struct Objects {
var sectionName : String!
var sectionObjects : [String]!
}
var objectArray = [Objects]()
for (key, value) in allDict {
objectArray.append(Objects(sectionName: key, sectionObjects: value))
}
The outcome is this:
ObjectArray: [__lldb_expr_134.Objects(sectionName: Section1, sectionObjects: ["device1", "device2"]), __lldb_expr_134.Objects(sectionName: Section2, sectionObjects: ["device3", "device4"])]
["device1", "device2"]
Now, the Problem is that I want to be able to rearrange the cells and I am able to rearrange the data in the array, but I have to get it back to the dictionary.
I'm aware that a dicitonary is unsorted, but while testing I never hat the problem that the devices changed there place in the array inside the dicitonary.
My idea is to rearrange the ObjectArray and then clear the dictionary and put the data back in.
So I tried to get the data from ObjectArray back using a for loop but I dont get it right, here is what I tried:
I found out I can access the data like this:
print(objectArray[0].sectionObjects)
["device1", "device2"]
So I tried:
for items in objectArray {
print(items)
for (name) in items.sectionObjects {
print(name)
}
}
there I get this:
device1
device2
But when I do:
for items in objectArray {
print(items)
for (name) in items.sectionName {
print(name)
}
}
I get this:
S
e
c
t
i
o
n
1
All I want is something like:
for (sectionNAme, sectionObjects) in objectArray {
newDictionary[sectionName] += [sectionObjects]
}
but this gets me this: Expression type '[Objects]' is ambiguous without more context
Sorry, for the long text...
You have to write the loop in this way
for object in objectArray {
newDictionary[object.sectionName] = object.sectionObjects
}
object represents one Objects instance – by the way it's recommended to name the struct in singular form Object – you get the name with object.sectionName and the objects with object.sectionObjects
Notes:
S
e
c
t
i
o
n
1
occurs because you treat the name string as array and you are iterating through the characters of the string.
Since you are using the implicit memberwise initializer anyway I would declare the struct with non-optional members as Section and shorten the member names:
struct Section {
var name : String
var objects : [String]
}

Save array of classes into Firebase database using Swift

I have an array of classes, which looks like this:
var myItems = [myClass]()
class myClass: NSObject {
var a: String?
var b: String?
var c: String?
var d: String?
}
What I want is to save the array called myItems into my database, and have every class inside of a personal section inside the database. Basically, I want every class to look like the one called "Eko" in this image:
To clarify, after "Eko" all the rest of the classes which is inside of the array myItems should be displayed. To achieve what the picture is demonstrating, I used this code:
let data = self.myItems[0]
let currU = FIRAuth.auth()?.currentUser?.uid
let userRef = self.ref.child("users").child(currU!).child(data.a!)
userRef.updateChildValues(["a": data.a!, "b": data.b!, "c": data.c!, "d": data.d!])
Obviously, this will only save the class at index 0 from the array myItems into the Firebase Database, which is displayed in the image above.
My question is thus, how do I save the entire array into the database? With my code I can only save 1 class from the array, and I would like to save all of the items into the database, so that they end up looking the same way that the one class does in the image. You could compare this to populating a tableView, where you need the "indexPath.row" to populate it with all the items instead of only one. I hope that I was clear enough!
You can't save a class into Firebase. But.. A class has a similar structure to a dictionary (properties and values, like key: value pairs etc).
Arrays in Firebase should generally be avoided - they have limited functionality and the individual elements cannot be accessed and for any changes you have to re-write the entire array.
Using a structure where the parent key names are created with childByAutoId is usually preferred.
The easiest solution is to simply add intelligence to the class so it would craft a dictionary and then save itself.
Craft a user class
UserClass
var name = String()
var food = String()
func saveToFirebase() {
let usersRef = myFirebase.child(users)
let dict = ["name": self.myName, "food", self.myFood]
let thisUserRef = usersRef.childByAutoId()
thisUserRef.setValue(dict)
}
}
and and array to store them
var usersArray = [Users]()
populate the array
var aUser = UserClass()
aUser.name = "Leroy"
aUser.food = "Pizza"
usersArray.append(aUser)
var bUser = UserClass()
bUser.name = "Billy"
bUser.food = "Tacos"
usersArray.append(bUser)
and then iterate over the array saving each user
for user in usersArray {
user.saveToFirebase()
}
this will result in
users
-Ykasokokkpoad
name: Leroy
food: Pizza
-YJlaok9sk0sd
name: Billy
food: Tacos
which is very similar to the structure you want. There are many other ways of creating this structure. For example, you could craft the entire dictionary in code and write it all out at one time.
Pardon typo's, I wrote this on the fly.
Firebase has no native support for arrays. If you store an array, it really gets stored as an "object" with integers as the key names.
// we send this
['hello', 'world']
// Firebase stores this
{0: 'hello', 1: 'world'}
Read this post for better understanding.