Skip to content

Instantly share code, notes, and snippets.

@sindresorhus
Created November 9, 2021 09:57
Show Gist options
  • Save sindresorhus/fc6b8c5966a569dd9f39faaaa7b3ba70 to your computer and use it in GitHub Desktop.
Save sindresorhus/fc6b8c5966a569dd9f39faaaa7b3ba70 to your computer and use it in GitHub Desktop.
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<T: AdditiveArithmetic & Comparable>(
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
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment