I've recently answered a question about reading elements from an array of arrays. A way it could be interpreted is that the OP wanted to read a range that could span over multiple subarrays of the 2D array like so :
let a = [["5", "3", ".", ".", "7", "."],
["6", ".", ".", "1", "9", "5"]]
a.lazy.flatMap{$0}[1..<7] ["3", ".", ".", "7", ".", "6"]
This way of reading a range would need to at least flatMap
all the previous arrays to the lower bound of the range. Which would be wasteful.
A more natural way would be to only read the needed elements from the original array:
func get<T>(_ range: Range<Int>, from array2d: [[T]]) -> [T]? {
var result: [T] = []
result.reserveCapacity(range.upperBound - range.lowerBound)
var count1 = 0
//Get the index of the first element
guard var low = array2d
.firstIndex(where: {
count1 += $0.count
return range.lowerBound < count1 }),
count1 != 0
else { return nil }
let before = count1 - array2d[low].count
var count2 = before
//Get the index of the last element in the range
guard let high = array2d[low..<array2d.endIndex]
.firstIndex(where: {
count2 += $0.count
return range.upperBound <= count2
}),
count2 != 0
else { return nil }
//Append the elements in the array with the low index
for i in (range.lowerBound - before)..<min(range.upperBound - before, array2d[low].count) {
result.append(array2d[low][i])
}
//If the range spans over multiple arrays
if count1 < count2 {
low += 1
//Append the elements in the arrays with an index between low and high
while low < high {
result.append(contentsOf: array2d[low])
low += 1
}
//Append the elements in the array with the high index
for i in 0..<(range.upperBound - count2 + array2d[high].count) {
result.append(array2d[high][i])
}
}
return result
}
Which could be used like so :
let a = [["0", "1", "2", "3", "4", "5"], ["6", "7"], [], ["8","9","10","11", "12"], ["13","14", "15"]]
get(5..<11, from: a) //Optional(["5", "6", "7", "8", "9", "10"])
get(7..<9, from: a) //Optional(["7", "8"])
To me, the above code feels.. on the border of sanity/maintainability...
What I'd like to do is to make it more generic, maybe as an extension to RandomAccessCollection
, and making the flattening process recursive for arrays/collections of arbitrary dimensions. I'm stuck here and not sure if this is the appropriate network to ask such a question.
Feedback on all aspects of the code is welcome, such as (but not limited to):
- Efficiency,
- Readability,
- Naming.