How to download an TreeView from server - ios

From my server I have a table like this,
objectId parentId id locationName
I am trying to make a treeview under iOS using RATreeView. Every single object(cell) will have its unique id, and every cell that under some other cell will have its parentId which equals to its father's Id. Right now I'm trying to get those data from my server and assemble them as a treeview during runtime. But I really do not have a good algorithm to solve this.
My algorithm is that get all the parentId in an array and id in another array, then find if there is anything equal in two arrays. But this comes with an problem, when there are more than two children under one cell, there will be duplicate parent cell. Anyone has any good idea?

I think it's better if you can get the response like this
objectId, [childrendObjectId] (array of children id) id locationName
You will do it easier.
But if you can't change the response, whenever you want to get the children of the object, just filter it.
I have a simple example here, hope it help
class MyObject {
var id = 1 // I don't know id or objectId is uniqueId, so I remove your objectId, just edit it
var parentId: Int?
var locationName: String?
init(id: Int) {
self.id = id
}
}
var array = [MyObject]()
for idx in 1...100 {
let myObj = MyObject(id: idx)
if idx > 10 {
myObj.parentId = Int(arc4random() % 10 + 1)
}
array.append(myObj)
}
func childrenOf(_ object: MyObject) -> [MyObject] {
return array.filter({$0.parentId == object.id})
}
let firstObject = array[0]
print(childrenOf(firstObject).map({$0.id}))

Related

How can I create an algorithm that appends a name onto an array?

I've been going through Glassdoors for companies I am interested in interviewing with and saw one that said the company asked them a question about "appending a name onto a list". I have been trying to figure out how to go about that. I know the function will take an array and will return an array as well. Can someone help me write that out and explain it to me? Attached is my version, which does not work. It just prints out the existing string twice
func addNameToList(_ names: [String]) -> [String] {
var names = ["Ben", "Sam", "Ken"]
var results = [names[0]]
for name in names {
names.append(name)
print(names)
}
return results
}
addNameToList([String("Louis")])
if you are need to add one or more string value with Existing string array you can use like below
var globalArrayString = ["One","two","three"]
func appendString(_ currentString: String...) -> [String] {
globalArrayString.append(contentsOf: currentString)
return globalArrayString
}
// if you need to add more String,Call like below
let combinedArray = appendString("four","five")
print(combinedArray)

How to update object in both array if I use separate array for search result?

I want add search functionality in my listing screen. Every item in listing is not view-only but, say for example an object whose few properties are changing based on user action.
To add search functionality, the general way is to keep separate array for search result. Now, in my case user can either alter few values of object from either by searching or without searching. In that case, I will have to update object in both the array based on id of that object but this solution doesn't seems to be proper.
What is the proper way to update the model in this case?
Here is my object
class Course : NSObject
{
var courseId : Int = 0
var name : String = ""
var chapters : [Chapter] = []
var estimatedTime : Int = 0
// Values which will be altered
var userSpentTime : Int = 0
var status : CourseStatus = .pending // Enum
init(_ json: Any?)
{
guard let data = json as? Dictionary else { return }
/* Assignment of values from JSON */
...
...
}
}

How can I properly copy objects from one Realm object to another object

Based on the following code I would like to be able to create a new ItemList from an existing one. In other words I have an ItemList called First List and I want to create a new ItemList, call it Second List and fill it with the Items from First List.
The way I have it right now is that it creates the Second List as expected, the Items from the First List show in Second List but what doesn't work is when I want to delete only the Items from First List, it deletes Items from both lists. I guess I'm not truly copying the items.
So the question is, how can I copy Items from First List to Second List?
Object Models:
class ItemList: Object {
dynamic var listName = ""
dynamic var createdAt = NSDate()
let items = List<Item>()
}
class Item:Object{
dynamic var productName:String = ""
dynamic var createdAt = NSDate()
}
Code to create Second List from First List
This Works fine, it creates Second List and adds the items from First List but I don't think I'm making copies just showing them in Second List.
let newList = ItemList()
newList.listName = "Second List"
if let selectedList = realm.objects(ItemList.self).filter("listName = %#", "First List").first{
let itemsFromFirstList = selectedList.items
newList.items.append(objectsIn:itemsFromFirstList)
}
try! realm.write {
realm.add(newList)
}
This code is supposed to delete only the items from First List
This actually deletes items from both First List and Second List
let listToDelete = realm.objects(ItemList.self).filter("listName = %#", "First List").first
try! realm.write {
for item in (listToDelete?.items)! {
realm.delete(realm.objects(Item.self).filter("productName = %#", item.productName).first!)
}
}
What you want to do is use:
for record in postsDB.objects(PostModel.self) {
if !combinedDB.objects(PostModel.self).filter("postId == \(record.parentId)").isEmpty {
combinedDB.create(PostModel.self, value: record, update: false)
}
}
The create method is inherited from Object. It tells the target to create a new object. Use true if you want it to look to see if there is already a record there, and update it if there is.
PostModel is the Object type, record is what you want copied.
Edit: I added the if statement to provide more context. You didn't show your class definitions, so I was guessing. This is a working example. I ask for a set of records from DatabaseA and copy it to DatabaseB (postsDB to combinedDB).
So if the type of the object you're trying to insert is a List, I'd recommend you define a subclass of Object, and have at least the list you need as a property.
class TagList: Object {
dynamic var tag = ""
var list = List<PostModel>()
override class func primaryKey() -> String? {
return "tag"
}
}
Full working example illustrating: creating new objects, copying all objects to a second list, deleting from second list after copying, adding to first list (which didn't get anything deleted from it.
import Foundation
import RealmSwift
class Letter: Object {
dynamic var letter = "a"
}
class Letters: Object {
var letterList = List<Letter>()
}
class ListExample {
let listRealmStore = try! Realm() // swiftlint:disable:this force_try
func testThis() {
print(Realm.Configuration.defaultConfiguration.fileURL!)
listRealmStore.beginWrite()
addSingleItems() // add 3 objects to the DB
let firstList = Letters()
let allObjects = listRealmStore.objects(Letter.self)
for item in allObjects {
firstList.letterList.append(item)
}
let secondList = Letters()
let itemsToCopy = firstList.letterList
for item in itemsToCopy {
let obj = listRealmStore.create(Letter.self)
obj.letter = item.letter
secondList.letterList.append(obj)
}
let third = Letter()
third.letter = "Z"
listRealmStore.add(third)
firstList.letterList.append(third)
secondList.letterList.removeLast()
do {
try listRealmStore.commitWrite()
} catch let error {
print("couldn't commit db writes: \(error.localizedDescription)")
}
print("list one:\n\(firstList)")
print("list two:\n\(secondList)")
}
func addSingleItems() {
for letter in ["a", "b", "c"] {
let objectToInsert = Letter()
objectToInsert.letter = letter
listRealmStore.add(objectToInsert)
}
}
}
Results in:
list one:
Letters {
letterList = List<Letter> (
[0] Letter {
letter = a;
},
[1] Letter {
letter = b;
},
[2] Letter {
letter = c;
},
[3] Letter {
letter = Z;
}
);
}
list two:
Letters {
letterList = List<Letter> (
[0] Letter {
letter = a;
},
[1] Letter {
letter = b;
}
);
}
Are you really trying to create copies of your items, or do you just want to be able to remove them from lists independently?
When you do:
newList.items.append(objectsIn: itemsFromFirstList)
you end up with the same objects being in both lists. List<T> just stores references to objects that live within the Realm. Appending an object to a List just references the existing object, it doesn't copy the object.
When you call Realm.delete(_:) you remove that object entirely from the Realm, not just from a single list that it is a member of. To remove an object from a List, you should instead use List.remove(objectAtIndex:).
One part the solution you are looking for could be like this, make copy objects in the list, or you can just use this idea to clone whole list it self:
Previously answered here
As of now, Dec 2020, there is not proper solution of this issue. We have many workarounds though.
Here is the one I have been using, and one with less limitations in my opinion.
Make your Realm Model Object classes conform to codable
class Dog: Object, Codable{
#objc dynamic var breed:String = "JustAnyDog"
}
Create this helper class
class RealmHelper {
//Used to expose generic
static func DetachedCopy<T:Codable>(of object:T) -> T?{
do{
let json = try JSONEncoder().encode(object)
return try JSONDecoder().decode(T.self, from: json)
}
catch let error{
print(error)
return nil
}
}
}
Call this method whenever you need detached / true deep copy of your Realm Object, like this:
//Suppose your Realm managed object: let dog:Dog = RealmDBService.shared.getFirstDog()
guard let detachedDog = RealmHelper.DetachedCopy(of: dog) else{
print("Could not detach Note")
return
}
//Change/mutate object properties as you want
detachedDog.breed = "rottweiler"
As you can see we are piggy backing on Swift's JSONEncoder and JSONDecoder, using power of Codable, making true deep copy no matter how many nested objects are there under our realm object. Just make sure all your Realm Model Classes conform to Codable.
Though its NOT an ideal solution, but its one of the most effective workaround.

Efficient way to filter array of Person objects based on an array of string names

I have an array of names:
var namesArray = ["Bert","Tony","Phil","George", "David"]
I then have an array of Person Objects:
var personsArray: [Person]
And a snippet of my Person class is:
class Person {
var name: String
...some code omitted...
}
I am looking for a way to filter my array of Persons objects to only include the Person whos name is found in the namesArray.
I considered using the .filter on the array but I need to loop over two arrays.
let filterByNameArray = persons.filter({
($0.name == //string)!
})
But I believe this is incorrect as I need to loop through the names array also. I solved my issue using a double for loop:
var pArray: [Person] = []
for person in personsArray {
for nameString in namesArray {
if person.name == nameString {
pArray.append(person)
}
}
}
However, this is ugly and uses a significant amount of CPU so my question is,is there a more efficient way to do this? :) Im sure there is.
Use the contains method on the namesArray to search all of it.
let filteredByNameArray = persons.filter {
namesArray.contains($0.name)
}

Swift Array Filtering

I have a Game Class with property id: String and im trying to filter the array gamesArray: [Game] based on ids contained in another array haveGameArray: [String] and return the results on gamesFilteredArray: [Game]
// Filter the array by the contained in self.haveGameArray
self.gamesFilteredArray = self.gamesArray.filter(
{
(game: Game) -> Bool in
let id = game.id
return self.haveGameArray.contains(id)
})
Its not working and i don't know what to do, because the logic its right

Resources