Created
August 19, 2021 19:52
-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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