https://bfe.dev is like a LeetCode for FrontEnd developers. I’m using it to practice my skills.
This article is about the coding problem BFE.dev#88. support negative Array index in JavaScript
Look at the example
const originalArr = [1,2,3]
const arr = wrap(originalArr)
arr[0] // 1
arr[1] // 2
arr[2] // 3
arr[3] // undefined
arr[-1] // 3
arr[-2] // 2
arr[-3] // 1
arr[-4] // undefined
We could return a new object and hard set the properties with values, like {0:1, 1:2, 2:3, -1:3, -2:2, -3:1}
.
But it looks impossible to sync the data to the original array as follows.
arr[-1] = 5
arr // [2,3,5]
originalArr // [2,3,5]
We could use getter/setter to do the sync, like:
Object.defineProperty(arr, '-1', {
get() {
return originalArr[2]
},
set(value) {
originalArr[2] = value
}
})
But there are not only -1
, but also -2
...-originalArr.length
. It is to maintain the hardcoded indices when length changes.
Proxy comes the rescue.
From the problem, we could notice that what we want is actually a Proxy
- we apply everything to the original array
- only for negative indices, we tweak a little
First let's write the basic Proxy:
function wrap(arr) {
return new Proxy(arr, {
get(target, prop) {
return target[prop]
},
set(target, prop, value) {
target[prop] = value
return true
}
})
}
This returns a proxy that proxies everything. So test cases for non-negative index actually passes, but not the negative case, of course.
Tweak the index
Notice the prop is not number, we convert it to the non-negative index first before we apply to the original array.
get(target, prop) {
let index = parseInt(prop, 10)
// negative index
if (index < 0) {
index += target.length;
return target[index]
}
return target[prop]
},
set(target, prop, value) {
let index = parseInt(prop, 10)
// negative index
if (index < 0) {
index += target.length;
// negative index overflowed, too small
if (index < 0) {
throw new Error('not ok')
}
target[index] = value
return true
}
target[prop] = value
// don't forget to return true to indicate success
return true
}
not iterable!
Oops, [...arr]
doesn't work on our proxy.
[...arr]
actually calls the Symbol.Iterator
on the target, since our proxy doesn't hold the data, it needs to run the iterator on the original array. Function.prototype.bind()
is a good choice.
get(target, prop) {
if (prop === Symbol.iterator) {
return target[Symbol.iterator].bind(target)
}
...
}
Passed!
This is pretty interesting problem, maybe you can also have a try at https://bfe.dev
Top comments (0)