DEV Community

jser
jser

Posted on • Updated on

BFE.dev challenge #88 support negative Array index in JavaScript

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

Alt Text

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
Enter fullscreen mode Exit fullscreen mode

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]
Enter fullscreen mode Exit fullscreen mode

We could use getter/setter to do the sync, like:

Object.defineProperty(arr, '-1', {
  get() {
     return originalArr[2]
  },
  set(value) {
     originalArr[2] = value
  }
})

Enter fullscreen mode Exit fullscreen mode

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

  1. we apply everything to the original array
  2. 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
    }
  })
}     
Enter fullscreen mode Exit fullscreen mode

This returns a proxy that proxies everything. So test cases for non-negative index actually passes, but not the negative case, of course.

Alt Text

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
}                   
Enter fullscreen mode Exit fullscreen mode

not iterable!

Alt Text

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)
  }
  ...
}
Enter fullscreen mode Exit fullscreen mode

Passed!

Alt Text

This is pretty interesting problem, maybe you can also have a try at https://bfe.dev

Top comments (0)