Clifton Labrum Clifton Labrum - 4 months ago 16
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()
cv1.name = "Day"
cv1.value = 1.2
let cv2 = CustomValuePack()
cv2.name = "Day"
cv2.value = 1.3
let cv3 = CustomValuePack()
cv2.name = "Night"
cv2.value = 2.2

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

entry1.customValues = [cv1, cv2]

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

let entry3 = EntryPack()
entry3.date = "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
value
for each
CustomValuePack
where the
name
is
"Day"
.

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

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 { $0.name == "Day" }
    .map { $0.value }
    .reduce(0, combine: +)

Improvements

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.