UPDATE: I realized the question is invalid. Ignore it. I made a mistake in the for
loop, it actually takes only ~1 ms to sum all the indexes, not half a second, that lead me to the assumption it can be optimized with binary search, which does not apply here.
Let's say we have:
- an integer
target = 85
- an array of integers
array = [36, 48, 48, 36, 48, 48, 48, 48, 48, 48]
How would we efficiently find the index
at which sum(array[0], array[N]) > target
? In this example, the index would be 2
because the sum of indexes exceeds the target
at index 2
.
Basically, I have a virtual container of elements (it renders only a small subset of elements at all times) all of which have different heights (36, 48, etc) and a scroll
event that returns the amount of scrolled pixels (target), so I'm trying to find the element to which the container is scrolled to, in this example the container is scroll 85px
down, which means it scrolled pass the element 2
.
Example:
let target = 85
let array = [36, 48, 48, 36, 48, 48, 48, 48, 48, 48]
// Finding the element with a bruteforce method:
array[0] > target // false
array[0] + array[1] > target // false
array[0] + array[1] + array[2] > target // true
return 2
For some reason trying to sum
all the indexes until we find the target value using a for
loop takes half a second per 1000 values.
I'm not sure if there's a well known algorithm for this already, but I figured I have to use a custom derivative form of a binary search to find the index in just a few iterations, for example:
Divide the array in half
Sum all the indexes in the left half
If the target < sum, it means the target is in the left half, so divide it again
If the target > sum, extend the search to the right half
Repeat recursively
I've partially created this recursive algorithm but I'm having difficulties figuring out how to properly handle the condition when the target is in the "right half" of the array, i.e. when target > halfSliceSum
:
let target = 85
let array = [36, 48, 48, 36, 48, 48, 48, 48, 48, 48]
console.log(getElementIndexAtTargetPosition(array, target))
function getElementIndexAtTargetPosition(array, target) {
// TESTS (expected results):
// ([36, 48, 48, 36, 48, 48, 48, 48, 48, 48], 35) => return 0
// ([36, 48, 48, 36, 48, 48, 48, 48, 48, 48], 36) => return 0
// ([36, 48, 48, 36, 48, 48, 48, 48, 48, 48], 37) => return 1
// ([36, 48, 48, 36, 48, 48, 48, 48, 48, 48], 84) => return 1
// ([36, 48, 48, 36, 48, 48, 48, 48, 48, 48], 85) => return 2
// ([36, 48, 48, 36, 48, 48, 48, 48, 48, 48], 456) => return 8
let halfIndex = Math.round(array.length / 2)
let halfSlice = array.slice(0, halfIndex)
let halfSliceSum = halfSlice.reduce((a, b) => a + b, 0)
if (array.length === 1) {
return 0
}
if (target === halfSliceSum) {
return halfIndex
}
else if (target < halfSliceSum) {
return getElementIndexAtTargetPosition(halfSlice, target)
}
else if (target > halfSliceSum) {
// Increase search slice by Math.round(halfIndex * 1.5)?
// ...
}
}
The target is always within the array, i.e. target <= sum(array[0], array[lastIndex])