Search code examples
swiftcore-data

Count all unique objects in CoreData?


How would the following SQL-query be implemented in CoreData?

-- table-definition
CREATE TABLE my_numbers (number INTEGER NOT NULL)

-- query
SELECT number, COUNT(1) as n FROM my_numbers GROUP BY number

So if I have a similar entity in CoreData called MyNumberEntity with an attribute called value of type Int16. Is there some way of using a predicate (or something else?) to achieve the same as the SQL-query above in a few lines?

The only way I can think of is to first fetch all MyNumberEntity into a set, then iterate over the set and using the iterator to get a count of respective value, storing it in an array/dictionary.

Is there an easier/neater way of doing this in CoreData?


Solution

  • You can use NSExpression

    // Create a fetch request for the entity you want to group and count
    let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "YourEntity")
    
    // Create an expression to perform the count
    let countExpression = NSExpression(format: "count:(yourAttribute)")
    
    // Create an expression description to define the result
    let countExpressionDescription = NSExpressionDescription()
    countExpressionDescription.name = "count"
    countExpressionDescription.expression = countExpression
    countExpressionDescription.expressionResultType = .integer32AttributeType
    
    // Set the properties to fetch in the fetch request
    fetchRequest.propertiesToFetch = ["yourAttribute", countExpressionDescription]
    
    // Set the grouping properties
    fetchRequest.propertiesToGroupBy = ["yourAttribute"]
    
    // Specify that the result type should be a dictionary
    fetchRequest.resultType = .dictionaryResultType
    
    // Execute the fetch request
    do {
        let results = try context.fetch(fetchRequest) as? [NSDictionary]
        if let results = results {
            for result in results {
                let attributeValue = result["yourAttribute"] as? String
                let count = result["count"] as? Int
                print("Attribute: \(attributeValue ?? "") Count: \(count ?? 0)")
            }
        }
    } catch {
        print("Fetch failed: \(error)")
    }