Marcus
Marcus

Reputation: 33

Swift Create new array based on two arrays under certain conditions

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

Answers (2)

Luca Angeletti
Luca Angeletti

Reputation: 59506

The input

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"]

What can go wrong

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
}

The function

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
    }
}

Usage

let res = try groupData(monthStrings, paymentsStrings: paymentStrings)
print(res) // [2018: 2000, 2017: 4000, 2016: 3000, 2019: 3000]

Update

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

Eendje
Eendje

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

Related Questions