Clifton Labrum Clifton Labrum - 1 year ago 64
Swift Question

Sum the Values of Nested Custom Objects in Swift

I am trying to add up the values found within some custom objects in Swift. I have the follow code working in a Playground:

//Custom Object
class EntryPack: NSObject{
var date: String!
var duration: Double!
var customValues:[CustomValuePack] = [] //Nested array of objects declared below

//Another object to nest in the above
class CustomValuePack: NSObject{
var name:String!
var value: Double!

//Instantiate nested objects
let cv1 = CustomValuePack() = "Day"
cv1.value = 1.2
let cv2 = CustomValuePack() = "Day"
cv2.value = 1.3
let cv3 = CustomValuePack() = "Night"
cv2.value = 2.2

//Instantiate parent objects
let entry1 = EntryPack() = "bingo"
entry1.duration = 1.1

entry1.customValues = [cv1, cv2]

let entry2 = EntryPack() = "bingo"
entry2.duration = 2.2

let entry3 = EntryPack() = "dang"
entry3.duration = 3.0

//Throw it all together into an array
var package = [entry1, entry2, entry3]

//Predicate for some filtering
let predicate = NSPredicate(format:"date = %@", "bingo")
let results = (package as NSArray).filteredArrayUsingPredicate(predicate) as! [EntryPack]

//Sum all the parent object durations
let sum = results.reduce(0.0) { $0 + $1.duration }

print(sum) // = 3.3

But now I want to add up the
for each
where the

I can't figure out how to do a similar reduce on a nested array of objects. I've tried something like this, but it yields a syntax error:

let sum2 = results.reduce(0.0) { $0.customValues + $1.customValues.value } //Error

How do I sum the value in a nested array of objects? I eventually want to apply an NSPredicate to filter by
name = Day
as well, but I'm not that far yet.

Answer Source

Given entries defined as follow

let entries = [entry1, entry2, entry3]

you can extract the CustomValues with name == "Day" and sum the value field with this code

let sum = entries
    .flatMap { $0.customValues }
    .filter { $ == "Day" }
    .map { $0.value }
    .reduce(0, combine: +)


You are using Swift but for some reason there still is a lot of Objective-C into your code.

This is how i would refactor your code

struct EntryPack {
    let date: String
    let duration: Double
    let customValues:[CustomValuePack]

struct CustomValuePack {
    let name:String
    let value: Double

let cv1 = CustomValuePack(name: "Day", value: 1.2)
let cv2 = CustomValuePack(name: "Day", value: 1.3)
let cv3 = CustomValuePack(name: "Night", value: 2.2)

let entry1 = EntryPack(date: "bingo", duration: 1.1, customValues: [cv1, cv2])
let entry2 = EntryPack(date: "bingo", duration: 2.2, customValues: [])
let entry3 = EntryPack(date: "dang", duration: 3.0, customValues: [])

Please note that now EntryPack and CustomValuePack are structs which are value types.