extension Collection where Element: Comparable { /** Returns the index of the first occurrence of the lowest value in the collection. ``` [4, 2, 3, 2, 5].firstIndexOfMinElement() //=> 1 ``` */ func firstIndexOfMinElement() -> Index? { zip(indices, self).min { $0.1 < $1.1 }?.0 } } extension Sequence { /** Split a sequence into the given number of chunks where the sum of each chunk, by getting a derived value of each element, are as close as possible. Order is not preserved. ``` list.splittingEvenly(chunkCount: 2, usingValue: \.height) ``` This can be useful to make a staggered/masonry grid where you want to make two columns, each with images of differing heights and identical widths, and try to make the two columns have as similar total height as possible. */ func splittingEvenly( chunkCount: Int, usingValue getValue: (Element) -> T ) -> [[Element]] { // This uses "Complete greedy number partitioning" algorithm: https://en.wikipedia.org/wiki/Greedy_number_partitioning#Exact_algorithm // Sort it in descending order. let sortedArray = sorted { getValue($0) > getValue($1) } var chunks = [[Element]](repeating: [], count: chunkCount) var chunkSums = [T](repeating: .zero, count: chunkCount) for element in sortedArray { let index = chunkSums.firstIndexOfMinElement() ?? 0 chunks[index].append(element) chunkSums[index] += getValue(element) } return chunks } }