donchan
donchan

Reputation: 357

How to convert array to another array in Swift

I want to convert beforeDataSet to afterDataSet in Swift. Could you tell me how to convert it?

struct BeforeData {
    var value: Int
    var date: Date
}

struct AfterData {
    var sumValue: Int
    var dateString: String
}

let beforeDataSet = [
    BeforeData(value: 100, date: date1), // 2022-01-01 12:00
    BeforeData(value: 150, date: date2), // 2022-01-01 14:11
    BeforeData(value: 120, date: date3), // 2022-01-02 07:00
    BeforeData(value: 120, date: date4), // 2022-01-02 12:00
    BeforeData(value: 200, date: date5), // 2022-01-03 08:00
]

let afterDataSet = [
    AfterData(sumValue: 250, dateString: "2022/1/1"),
    AfterData(sumValue: 240, dateString: "2022/1/2"),
    AfterData(sumValue: 200, dateString: "2022/1/3"),
]

Upvotes: 0

Views: 1677

Answers (2)

Rob
Rob

Reputation: 438407

If you are grouping these for grouping these in the UI, I would suggest a few things:

  1. AfterData should use Date, not String.
  2. When you present this in the UI, use a user-friendly date formatter rather than the cryptic yyyy/MM/dd format. E.g., a dateStyle of .medium.
  3. I would use reduce to calculate the totals by day, sort to sort them, and map to create your custom objects.
  4. FWIW, using this pattern, you are no longer dependent upon the input data having already been sorted.

So, perhaps:

struct BeforeData {
    var value: Int
    var date: Date
}

struct AfterData {
    var sum: Int
    var date: Date    // note, not `String`
}

let iso = ISO8601DateFormatter()
let date1 = iso.date(from: "2022-01-01T12:00:00Z")!
let date2 = iso.date(from: "2022-01-01T14:11:00Z")!
let date3 = iso.date(from: "2022-01-02T07:00:00Z")!
let date4 = iso.date(from: "2022-01-02T12:00:00Z")!
let date5 = iso.date(from: "2022-01-03T08:00:00Z")!

let before = [
    BeforeData(value: 100, date: date1),
    BeforeData(value: 150, date: date2),
    BeforeData(value: 120, date: date3),
    BeforeData(value: 120, date: date4),
    BeforeData(value: 200, date: date5)
]

let calendar = Calendar.current

let grouped = before
    .reduce(into: [Date: Int]()) { dictionary, object in
        let day = calendar.startOfDay(for: object.date)
        dictionary[day, default: 0] += object.value
    }
    .sorted { $0.0 < $1.0 }
    .map { AfterData(sum: $0.value, date: $0.key) }

And then:

let formatter = DateFormatter()
formatter.dateStyle = .medium

for value in grouped {
    print(formatter.string(from: value.date), value.sum)
}

And a US user will see:

Jan 1, 2022 370
Jan 2, 2022 120
Jan 3, 2022 200

But a UK user will see:

1 Jan 2022 370
2 Jan 2022 120
3 Jan 2022 200

Then we are showing dates in the format with which the end-user is comfortable.

Upvotes: 0

Leo Dabus
Leo Dabus

Reputation: 236548

What you need is to reduce your original array (considering their elements are sorted by date) and check if the last date is in the same day as the current element date. If true sum the element value with the last element value otherwise append a new element to the result with the current element value and the formatted date string:

let afterDataSet: [AfterData] = beforeDataSet.reduce(into: []) {
    let dateString = Formatter.date.string(from: $1.date)
    if let index = $0.indices.last,
       $0[index].dateString == dateString {
        $0[index].sumValue += $1.value
    } else {
        $0.append(.init(sumValue: $1.value, dateString: dateString))
    }
}

You will need to add these Date Formatter to your project:

extension Formatter {
    static let date: DateFormatter = {
        let formatter = DateFormatter()
        formatter.calendar = Calendar(identifier: .iso8601)
        formatter.locale = .init(identifier: "en_US_POSIX")
        formatter.dateFormat = "yyyy-M-d"
        return formatter
    }()
}

Upvotes: 1

Related Questions