Swift group Realm query results

China☆狼群 提交于 2019-12-25 01:46:21

问题


I've following Realm objects (init and other non-essential properties removed for brevity) in my Swift app

@objcMembers
class Person: Object {
    dynamic var id: String = ""
    dynamic var name: String = ""

    override static func primaryKey() -> String? {
        return "id"
    }
}

@objcMembers
class Project: Object {
    dynamic var projectId: Int = 0
    dynamic var manager: Person?
    var tasks = List<Task>()
    dynamic var lastTask: Task?

    override static func primaryKey() -> String? {
        return "projectId"
    }
}

@objcMembers
class Task : Object {
    dynamic var project = LinkingObjects(fromType: Project.self, property: "tasks")
    dynamic var taskId: String = ""
    dynamic var description: String = ""
    dynamic var createDate: Date = Date()

    override static func primaryKey() -> String? {
        return "taskId"
    }
}

So if there are 2 project members

Person(id: 1, name: "Foo")

Person(id: 2, name: "Bar")

and multiple projects,

Project(projectId: 100, manager: , messages: [a1, a2, a3], lastMessage: )

a1 = Task(project: <#100>, taskId: "a1", description: "Task 1 about project 100", createDate: Date() )

a2 = Task(project: <#100>, taskId: "a2", description: "Task 2 about project 100", createDate: Date() )

a3 = Task(project: <#100>, taskId: "a3", description: "Task 3 about project 100", createDate: Date() )

Project(projectId: 101, manager: , messages: [a1, a2, a3], lastMessage: )

b1 = Task(project: <#101>, taskId: "b1", description: "Task 1 about project 101", createDate: Date() )

b2 = Task(project: <#101>, taskId: "b2", description: "Task 2 about project 101", createDate: Date() )

b3 = Task(project: <#101>, taskId: "b3", description: "Task 3 about project 101", createDate: Date() )

b4 = Task(project: <#101>, taskId: "b3", description: "Task 3 about project 101", createDate: Date() )

Project(projectId: 102, manager: , messages: [a1, a2, a3], lastMessage: )

c1 = Task(project: <#102>, taskId: "c1", description: "Task 1 about project 102", createDate: Date() )

Project(projectId: 103, manager: , messages: [a1, a2, a3], lastMessage: )

d1 = Task(project: <#103>, taskId: "d1", description: "Task 1 about project 103", createDate: Date() )

d2 = Task(project: <#103>, taskId: "d2", description: "Task 2 about project 103", createDate: Date() )

d3 = Task(project: <#103>, taskId: "d3", description: "Task 3 about project 103", createDate: Date() )

In my ProjectsViewController

I can get my Realm results as

func getProjects() -> Results<Project> {
    let results: Results<Conversation> = database
        .objects(Conversation.self)
        .sorted(byKeyPath: "lastTask.createDate", ascending: false)

    return results
}

[Side note - If there is a better way of sorting the results based on last item of tasks List then do let me know. That'll make the use lastTask var redundant.]

which will display in my table view as

===================
Projects
-------------------
Foo 
Project 100
Task a3 
-------------------
Bar 
Project 101
Task b4
-------------------
Foo
Project 102
Task c1 
-------------------
Bar
Project 103
Task d3 
-------------------

Question: How do I group the results in Realm query so I get a dictionary of grouped Array of results, such as

Foo -> [Project 100, Array of Tasks], [Project 102, Array of Tasks]

Bar -> [Project 101, Array of Tasks], [Project 103, Array of Tasks]

and they're tracked via NotificationToken for all inserts / updates. Also I want to dislay them grouped by sections in the table view.

===================
Projects (Grouped)
-------------------
Foo (Section Header)
-------------------
Project 100
Task a3 
-------------------
Project 102
Task c1 
-------------------


-------------------
Bar (Section Header)
-------------------
Project 101
Task b4
-------------------
Project 103
Task d3 
-------------------

回答1:


I'll answer based on what I think you're asking.

If I understand correctly, you want to find a dictionary with the Person as the key, the values being a dictionary with Project ID (or Project) as the key and values of an array of tasks. i.e.

let result : [Person:[Project:[Task]]] = .....

If this isn't correct, you can probably take the ideas here and massage them into your ideal.

As you want to key by Person, I'd add a reverse lookup property to the Person class - and this is probably the main thing you're missing. Look at this section of the manual for guidance: (https://realm.io/docs/swift/latest/#inverse-relationships), and then add this property to Person:

let projects = LinkingObjects(fromType: Project.self, property: "manager")

I would then add a computed property to Person to retrieve the projects and tasks. This creates an array since that was asked for in the question.

var projectsAndTasks : [Project:[Task]]
{
  var result : [Project:[Task]] = [:]
  for project in projects
  {
    result[project] = project.tasks.map { $0 }
  }

  return result
}

If you want the tasks to be sorted, then add a computed property to Projects called sortedTasks that does that for you.

Once that's done, you can add a function somewhere (either global or static to Person) to add the results for all objects of Person into a single dictionary. E.g. :

extension Realm
{
  var allPersonsAndProjects : [Person:[Project:[Task]]]
  {
    var result : [Person:[Project:[Task]]] = []
    let persons = objects(Person.self)
    for person in persons
    {
      result[person] = person.projectsAndTasks
    }

    return result
  }
}

Forgive any compile errors.



来源:https://stackoverflow.com/questions/55182360/swift-group-realm-query-results

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!