2

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

2
  • 2
    What type is year? Is it [NSDate] or just [String]? Either way, I would use a dictionary instead like: [Int: Double]. Commented Feb 28, 2016 at 19:33
  • for payment sum you'll use paymentSum.reduce(0, combine: +) to get the sum Commented Feb 28, 2016 at 19:34

2 Answers 2

3

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
}
Sign up to request clarification or add additional context in comments.

1 Comment

Many thanks to all of you for the quick response. The reason why I'm using arrays instead of Dics is that I need to populate a Tableview with the results. To my understanding this is much easier with arrays than dictionaries, especially if the table view must show a sorted order. Maybe I'm wrong.
0

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]

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.