Reputation: 33
I need some help in order to establish a new array in Swift 2.0.
I have two arrays, one contains dates and the other payments at this date.
let year = [February 2016, March 2016, June 2017, October 2017, January 2018, April 2019] // Data at which a payment is initiated
let payment = ["1000","2000,"3000","1000","2000,"3000"] // payment amount at date in array year
I'd like to create two new arrays in Swift code based on this. The final result should look like this:
let yearSum [2016, 2017, 2018,2019] // only full year
let paymentSum ["3000","4000","2000","3000"] // sum of all payment in the year
The array "yearSum" should contain only the full year number, while "paymentSum" should contain the sum of all payments in the year.
Has anybody an advice how I can code this?
Many thanks
Upvotes: 2
Views: 229
Reputation: 59506
First of all let's assign good names to the input constants
let monthStrings = ["February 2016", "March 2016", "June 2017", "October 2017", "January 2018", "April 2019"]
let paymentStrings = ["1000", "2000", "3000", "1000", "2000", "3000"]
We are working with strings as input, so many things could go wrong during the parsing of a Date or of an Int. For clarity lets define the following enum
enum Error: ErrorType {
case InputParamsHaveDifferentSizes(Int, Int)
case FirstParamHasInvalidDate
case SecondParamHasInvalidInt
}
func groupData(monthStrings: [String], paymentsStrings:[String]) throws -> [Int:Int] {
// make sure both arrays have the same size
guard monthStrings.count == paymentStrings.count
else { throw Error.InputParamsHaveDifferentSizes(monthStrings.count, paymentStrings.count) }
let formatter = NSDateFormatter()
formatter.dateFormat = "MMM yyyy"
// creates dates: an array of NSDate representing monthStrings
// if dates has a different size of monthsString then throws and error
guard
case let dates = (monthStrings.flatMap { formatter.dateFromString($0) })
where dates.count == monthStrings.count
else { throw Error.FirstParamHasInvalidDate }
// creates payments: an array of Int representing paymentsStrings
// if payments has a different size of paymentsStrings then throws and error
guard
case let payments = (paymentStrings.flatMap { Int($0) })
where payments.count == paymentStrings.count
else { throw Error.SecondParamHasInvalidInt }
// put togheter dates and payments and group the results by year
return zip(dates, payments).reduce([Int:Int]()) { (var result, elm) -> [Int:Int] in
let year = NSCalendar.currentCalendar().components([NSCalendarUnit.Year], fromDate: elm.0).year
result[year] = elm.1 + (result[year] ?? 0)
return result
}
}
let res = try groupData(monthStrings, paymentsStrings: paymentStrings)
print(res) // [2018: 2000, 2017: 4000, 2016: 3000, 2019: 3000]
In the comment below you say you need to access the keys by index and you need them sorted so
let sortedKeys = res.keys.sort()
func value(index:Int) -> String? {
let key = sortedKeys[index]
let value = res[key]
return value
}
Upvotes: 3
Reputation: 8883
Well, due to lack of response I'll just assume years
is an array of strings:
// Data
let years = ["February 2016", "March 2016", "June 2017", "October 2017", "January 2018", "April 2019"]
let payed = ["1000", "2000", "3000", "1000", "2000", "3000"]
// Preparation
var result: [String: Double] = [:]
let digits = NSCharacterSet.decimalDigitCharacterSet().invertedSet
// Results
let yearOnly = years.flatMap { $0.componentsSeparatedByCharactersInSet(digits).filter { !$0.isEmpty } }
zip(yearOnly, payed).forEach { result[$0.0] = (result[$0.0] ?? 0) + (Double($0.1) ?? 0) }
Note that yearOnly
is created by filtering out all non-digits, so if you'll have days inside of years
then this won't work and you'll have to use another method to filter out the years.
// Output
["2018": 2000.0, "2019": 3000.0, "2016": 3000.0, "2017": 4000.0]
Upvotes: 0