Search code examples
swiftrealmnsarraygrouping

Get array(from Realm) of custom class grouped by some specific property


My Model looks like this-

class Model: Object {
    dynamic var id: String?
    dynamic var sessionId: String?

    static var all: [Model] {
        get {
            // Get all and return them
        }
    }

    static func getAllModelsGroupedBySessionId() -> [[Model]] {
        let all = Model.all

        var set = Set<String>()
        for element in all {
            if let sessionId = element.sessionId {
                set.insert(sessionId)
            }
        }

        let uniqueSessionIds = set.map { $0 }

        var array: [[Model]] = []

        for sessionId in uniqueSessionIds {
            var arr:[Model] = []
            for element in all {
                if element.sessionId == sessionId {
                    arr.append(element)
                }
            }
            array.append(arr)
        }
        return array
    }
}

My po Model.all is:

▿ 3 elements
  ▿ 0 : Model {
    id = one;
    sessionId = A;
}
  ▿ 1 : Model {
    id = two;
    sessionId = B;
}
  ▿ 2 : Model {
    id = three;
    sessionId = B;
}

And po Model. getAllModelsGroupedBySessionId()

▿ 2 element
  ▿ 0 : 1 elements
    ▿ 0 : Model {
    id = one;
    sessionId = A;
  }
  ▿ 1 : 2 elements
    ▿ 0 : Model {
    id = two;
    sessionId = B;
  }
    ▿ 1 : Model {
    id = three;
    sessionId = B;
  }

So my getAllModelsGroupedBySessionId works great but I want to if Realm or Array provides some api for the same work.

arrayOfArrays = Model. getAllModelsGroupedBySessionId()

An example is-

[[allmodelsWithSessionIdAsOne], [allmodelsWithSessionIdAsTwo]]

I don't want to sort them, I want an "array of arrays", where there is an array of, array of elements of with same session id.

I want to later populate that array in UICollectionView, where arrayOfarrays.count is number of section and arrayOfArray[section].count is number of item in that section.


Solution

  • Realm makes it easy to use relations for grouping. Because Realm is very cheap to fetch relations unlike SQL. In other words, instead of grouping when fetching, save it in a grouped state in advance.

    When saving the Model object, You can add code to do grouping at the same time like the following.

    let model = Model()
    model.sessionId = "..."
    ...
    

    Group and addModel() implemetation is here:

    Group.addModel(model)
    
    class Model: Object {
        dynamic var id: String?
        dynamic var sessionId: String?
    }
    
    class Group: Object {
        dynamic var sessionId: String?
        let models = List<Model>()
    
        static override func primaryKey() -> String? {
            return "sessionId"
        }
    
        static func addModel(_ model: Model) {
            let realm = try! Realm()
            if let sessionId = model.sessionId {
                if let group = realm.object(ofType: Group.self, forPrimaryKey: sessionId) {
                    try! realm.write {
                        group.models.append(model)
                    }
                } else {
                    let group = Group()
                    group.sessionId = sessionId
                    group.models.append(model)
                    try! realm.write {
                        realm.add(group)
                    }
                }
            }
        }
    }
    

    You can get same grouped data structure you mentioned, just fetch Group objects.

    realm.objects(Group.self)