0

I have a sorted array

let things = [
    Thing(activity: "1", name: "value1"),
    Thing(activity: "1", name: "value2"),
    Thing(activity: "1", name: "value3"),
    Thing(activity: "2", name: "value4"),
    Thing(activity: "2", name: "value5"),
    Thing(activity: "3", name: "value6"),
    Thing(activity: "3", name: "value7"),
    Thing(activity: "1", name: "value8"),
    Thing(activity: "1", name: "value9"),
    Thing(activity: "1", name: "value10")
 ]

I would like to produce array of arrays splitted when the activity value changes similar to the following

[[Thing(activity: "1", name: "value1"),
  Thing(activity: "1", name: "value2"),
  Thing(activity: "1", name: "value3")],
 [Thing(activity: "2", name: "value4"),
  Thing(activity: "2", name: "value5")],
 [Thing(activity: "3", name: "value6"),
  Thing(activity: "3", name: "value7")],
 [Thing(activity: "1", name: "value8"),
  Thing(activity: "1", name: "value9"),
  Thing(activity: "1", name: "value10")]]
1
  • Use reduce(into:). Commented Aug 14, 2019 at 20:28

2 Answers 2

4

A generalized solution would be:

extension Sequence {
    func grouped<T: Equatable>(by block: (Element) throws -> T) rethrows -> [[Element]] {
        return try reduce(into: []) { result, element in
            if let lastElement = result.last?.last, try block(lastElement) == block(element) {
                result[result.index(before: result.endIndex)].append(element)
            } else {
                result.append([element])
            }
        }
    }
}

Then you can do:

let results = things.grouped { $0.activity }

A less elegant (but slightly more efficient) solution would be:

extension Sequence {
    func grouped<T: Equatable>(by block: (Element) throws -> T) rethrows -> [[Element]] {
        var results: [[Element]] = []

        var lastValue: T?
        var index = results.endIndex
        for element in self {
            let value = try block(element)
            if let lastValue = lastValue, lastValue == value {
                results[index].append(element)
            } else {
                results.append([element])
                index = results.index(before: results.endIndex)
                lastValue = value
            }
        }
        return results
    } 
}
Sign up to request clarification or add additional context in comments.

1 Comment

Good answer . I will use Leo's answer as it solved my problem immediately. Thank you.
3

As already mentioned by @matt in comments you can use collection method reduce(into:) to group your elements by checking if the activity of the last element of the last array is equal to the current element activity, if so just append a new element to the last array, otherwise append a new array with a single element to the outer array:

struct Thing {
    let activity, name: String
}

let things: [Thing] = [
    .init(activity: "1", name: "value1"),
    .init(activity: "1", name: "value2"),
    .init(activity: "1", name: "value3"),
    .init(activity: "2", name: "value4"),
    .init(activity: "2", name: "value5"),
    .init(activity: "3", name: "value6"),
    .init(activity: "3", name: "value7"),
    .init(activity: "1", name: "value8"),
    .init(activity: "1", name: "value9"),
    .init(activity: "1", name: "value10")]

let grouped: [[Thing]] = things.reduce(into: []) {
    $0.last?.last?.activity == $1.activity ?
    $0[$0.index(before: $0.endIndex)].append($1) :
    $0.append([$1])
}

print(grouped)  // "[[__lldb_expr_1.Thing(activity: "1", name: "value1"), __lldb_expr_1.Thing(activity: "1", name: "value2"), __lldb_expr_1.Thing(activity: "1", name: "value3")], [__lldb_expr_1.Thing(activity: "2", name: "value4"), __lldb_expr_1.Thing(activity: "2", name: "value5")], [__lldb_expr_1.Thing(activity: "3", name: "value6"), __lldb_expr_1.Thing(activity: "3", name: "value7")], [__lldb_expr_1.Thing(activity: "1", name: "value8"), __lldb_expr_1.Thing(activity: "1", name: "value9"), __lldb_expr_1.Thing(activity: "1", name: "value10")]]\n"

1 Comment

Worked like charm!. Thanks.

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.