У меня есть отсортированный массив

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

Я хотел бы создать массив массивов, разделенных при изменении значения активности, как показано ниже

[[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
محمد الاشرف أنور 14 Авг 2019 в 23:20

2 ответа

Лучший ответ

Как уже упоминалось @matt в комментариях, вы можете использовать метод коллекции Reduce (into :) для группировки ваших элементов, проверяя, равна ли активность последнего элемента последнего массива текущей активности элемента, если это так, просто добавьте новый элемент к последнему массиву, иначе добавьте новый массив с единственным элементом к внешнему массиву:

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
Leo Dabus 15 Авг 2019 в 11:29

Обобщенным решением будет:

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

Тогда вы сможете:

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

Менее элегантное (но немного более эффективное) решение будет:

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
    } 
}
2
Rob 15 Авг 2019 в 00:20