2

How to filter duplicate category element into different array and count their amount?

This is the format, record is from the core data.

var record = [Record]()

[<Record:...; data: {accountbook = "MyBook";
                     amount = "10.50";
                     category = "A";
                     id = 1; 
},<Record:...; data: {accountbook = "MyBook";
                     amount = "5.50";
                     category = "B";
                     id = 2;
},<Record:...; data: {accountbook = "MyBook";
                     amount = "4.50";
                     category = "B";
                     id = 3;
}]

What I want

var category = ["A", "B"] //success
var total = [10.50, 10.00]

This is what I do for finding the category, and it works but how to group the same category and sum the total?

var category =[String]()
for categoryObject in record{
    if let categoryItem = categoryObject.category{
        category.append(categoryItem)
    }
}

//I tried this code to group the same category but fail. 

let result = Set(record).map{ category in return record.filter{$0 == category} }

Another way is this. but how if I have A-Z category? it will have very long code..Is there any way can detect the same value and split it to different array so that I can sum it by category.

categoryFilter = record.filter { $0.category!.contains("A") }
Lester
  • 701
  • 1
  • 9
  • 47

4 Answers4

0

First group your record object by category like this

extension Sequence {
    func group<GroupingType: Hashable>(by key: (Iterator.Element) -> GroupingType) -> [[Iterator.Element]] {
        var groups: [GroupingType: [Iterator.Element]] = [:]
        var groupsOrder: [GroupingType] = []
        forEach { element in
            let key = key(element)
            if case nil = groups[key]?.append(element) {
                groups[key] = [element]
                groupsOrder.append(key)
            }
        }
        return groupsOrder.map { groups[$0]! }
    }
}

Then you will get your distinct arrays based on category like this

var records : [Record] = []// your objects
let distinctRecords = records.group(by: {$0. category})

Now you can use reduce to calculate sum of values of that category

for items in distinctRecords{
    let sum = items.reduce(0.0){$0.0 + $1. amount ?? 0.0}// assuming you have float values in your amount
    print(items)// do whatever you want to do with your distinct array
    print(" \(sum)")
}
Fahadsk
  • 1,099
  • 10
  • 24
  • On the record.group(by:{$0.category}), I'm getting this `Cannot convert value of type '(_) -> _' to expected argument type '(Record) -> _'` – Lester Nov 24 '17 at 05:47
  • are you getting your array of objects like this `var records:[record] = []//intialize with objects of record` – Fahadsk Nov 24 '17 at 05:55
0

@Wan Jern I have written a piece of code, you can try this one. Hopefully, it will work.

var category = [String]()
var totalArr = [CGFloat]()
for categoryObject in record{
    if let categoryItem = categoryObject.category{
        if !category.contains(categoryItem) {
            category.append(categoryItem)
            totalArr.append(categoryObject.amount)
        } else {
            let index = category.index(of: categoryItem)
            let itemAtIndex = category[index]
            let itemAtIndex = itemAtIndex + categoryObject.amount
            totalArr.insert(itemAtIndex, at: index)
        }
    }
}
Shiv Jaiswal
  • 539
  • 1
  • 6
  • 22
0

Do you have your record struct in a class model?

like my data model selected from sqlite:

//Data model
import Foundation
import UIKit
class scoreModel: NSObject {
    var lessonName:String = String()
    var lessonCode:String = String()
    var creditPoint:Double = Double()
    var totalStudentNumber:Int = Int()
    var teacherName:String = String()
    var semesterName:String = String()
    var scoreValue:String = String()
    var studentCount:Int = Int()
}

If the answer is yes, we can use pointer in array and repeat while loop to do this manually.

Like my code:

let mysql = ""
let dataArray = SQLiteManager.shareInstance.queryDB(sql:mysql)
var i = 0

while i<dataArray.count-1
{
    var scoreArray = [Dictionary<String, Int>]()
    var range = 0
    var test = 0
    test = i
    //print("pointer i is'\(test)'")

    while ((dataArray[test]as! scoreModel).lessonCode == (dataArray[test+range]as! scoreModel).lessonCode && (test+range)<dataArray.count-1)
    {
        let key = (dataArray[test+range]as! scoreModel).scoreValue
        let value = (dataArray[test+range]as! scoreModel).studentCount
        var dict: [String: Int] = [String: Int]()
        dict[key] = value
        scoreArray.append(dict)
        //print("working pointer is'\(test+range)'")
        range = range+1
    }

    //transfer array
    let model:resultModel = resultModel()
    model.lessonName = (dataArray[test]as! scoreModel).lessonName
    model.lessonCode = (dataArray[test]as! scoreModel).lessonCode
    model.creditPoint = (dataArray[test]as! scoreModel).creditPoint
    model.semesterName = (dataArray[test]as! scoreModel).semesterName
    model.teacherName = (dataArray[test]as! scoreModel).teacherName
    model.totalStudentNumber = (dataArray[test]as! scoreModel).totalStudentNumber
    model.scoreArray = scoreArray
    resultArray.add(model)
    i = i+range
    //print("range is'\(range)'")
}
Alex Li
  • 55
  • 1
  • 9
0

Make the Records as Hashable with "category" as the unique in stubs function

struct Record: Hashable {

    var accountbook = ""
    var category = ""
    var amount = 0.0
    // added from stubs of Hashable
    var hashValue: Int { return category.hashValue }

    static func ==(lhs: Record, rhs: Record) -> Bool {
        return lhs.category == rhs.category
    }
}

Then filter the unique categories

let categories = Set(record).map { $0.category }
print(categories) // ["B", "A"]

And make a sum of each category

let totals = categories.map { c in
    record.filter { $0.category == c }.map{ $0.amount }.reduce(0, +)
}
print(totals) // get the sums as [10.0, 10.5]
xmhafiz
  • 3,482
  • 1
  • 18
  • 26