Skip to content

Instantly share code, notes, and snippets.

@ppeelen
Created August 19, 2021 19:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ppeelen/5a3ac69c10b61c0bd10a8b1ef8f5a8b7 to your computer and use it in GitHub Desktop.
Save ppeelen/5a3ac69c10b61c0bd10a8b1ef8f5a8b7 to your computer and use it in GitHub Desktop.
This extension will partition an array into an array with sub items depending on the parent item. It is mean to de-flatten a flattened array.
import Foundation
// The definition of our struct
struct FooBar: Equatable{
enum ItemType {
case main, sub
}
let itemType: ItemType
let name: String
}
// The list we are going to partition
let list: [FooBar] = [
FooBar(itemType: .main, name: "Main 1"),
FooBar(itemType: .sub, name: "Sub 1 item 1"),
FooBar(itemType: .sub, name: "Sub 1 item 2"),
FooBar(itemType: .sub, name: "Sub 1 item 3"),
FooBar(itemType: .sub, name: "Sub 1 item 4"),
FooBar(itemType: .sub, name: "Sub 1 item 5"),
FooBar(itemType: .sub, name: "Sub 1 item 6"),
FooBar(itemType: .main, name: "Main 2"),
FooBar(itemType: .sub, name: "Sub 2 item 1"),
FooBar(itemType: .sub, name: "Sub 2 item 2"),
FooBar(itemType: .main, name: "Main 3"),
FooBar(itemType: .sub, name: "Sub 3 item 1"),
FooBar(itemType: .sub, name: "Sub 3 item 2"),
FooBar(itemType: .sub, name: "Sub 3 item 3"),
]
// Setting up the
let mains = list.filter { $0.itemType == .main }
var newList: [[FooBar]] = list.partition(by: mains)
extension Array where Element: Equatable {
/// Partition self into chucks defined by `subSelf`
/// - Parameter subSelf: The items to divide into, if item is not found in array the subSelf is disregarded.
/// - Returns: [[Element]]
func partition(by subSelf: [Element]) -> [[Element]] {
subSelf.map { subItem -> [Element] in
guard let index = self.firstIndex(of: subItem) else { return [] }
let endIndex: Index
if let nextMainItem = subSelf.nextItem(after: subItem), let nextEndIndex = self.firstIndex(of: nextMainItem) {
endIndex = nextEndIndex
} else {
endIndex = list.count
}
return self.stride(from: index, to: endIndex, by: 1)
}
}
/// Get the next item in array after self
/// - Parameter item: The current item to search for
/// - Returns: The next item or nil
func nextItem(after item: Element) -> Element? {
// Could be done in one line using:
// firstIndex(of: item).flatMap { $0 + 1 < count ? self[$0 + 1] : nil }
// but would be less readable.
if let index = self.firstIndex(where: { $0 == item }), index + 1 < self.count {
return self[index + 1]
}
return nil
}
/// Returns a sequence from array, from starting value to, but not including, an end value, stepping by the specified amount.
/// - Parameters:
/// - from: The starting index to use for the sequence. If the sequence contains any values, the first one is start.
/// - to: An end index to limit the sequence. end is never an element of the resulting sequence.
/// - by: The amount to step by with each iteration. A positive stride iterates upward; a negative stride iterates downward.
/// - Returns: A sequence from start toward, but not including, end. Each value in the sequence steps by stride.
func stride(from: Array<Element>.Index, to: Array<Element>.Index, by: Int) -> [Element] {
Swift.stride(from: from, to: to, by: by).map { self[$0] }
}
}
dump(newList)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment