BFE.dev解答記録 #88. JavaScriptでnegative indexをサポートする

2 min読了の目安(約2600字TECH技術記事

https://bfe.dev/ja はFrontEnd版のLeetCode、GAFAの面接を受けるなら練習した方がいいかなと。
以下は自分の練習記録です。

スクリーンショット 2020-10-09 15.57.52.jpg

BFE.dev#88. JavaScriptでnegative indexをサポートする をみてみよう

説明をみよう

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

新しいobjetを返して、プロパティにいい感じのindexとvalueを設定するのは可能です。例えば {0:1, 1:2, 2:3, -1:3, -2:2, -3:1}

だけど下記のように元々の配列とシンクするのは不可能のようだ。

arr[-1] = 5
arr // [2,3,5]
originalArr // [2,3,5]

getter/setterを使えばsyncできそう、このように

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

だけの-1の他、-2... -originalArr.lengthもあるので、全部いちいち変更するのは無理を感じる。

Proxy は救世主

問題の説明からでは、ある意味のProxyが求められるのはわかりました。

  1. 全ての変更は元のarrayにする
  2. negative indexのみ、戻り値をいじる

なず基本のproxyを書く

function wrap(arr) {
  return new Proxy(arr, {
    get(target, prop) {
      return target[prop]
    },
    
    set(target, prop, value) {
      target[prop] = value
      return true
    }
  })
}     

このproxyはただproxyするだけなので、negative indexはサポートしていない、それ以外のデータはちゃんと返している。

Alt Text

indexをいじる

propはnumberではないことを注意してください、まずnumberに変換しとこう。


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
}                   

iterableではない!!

Alt Text

[...arr]は動かない。。

[...arr] は実はSymbol.Iteratorを実行している、上記のproxyではdata持っていないから、元の配列のinteratorを実行する必要があるので、Function.prototype.bind() はいいかも、このように。

get(target, prop) {
  if (prop === Symbol.iterator) {
    return target[Symbol.iterator].bind(target)
  }
  ...
}

通った!

Alt Text

上記のコードはこちらで見れます。https://bigfrontend.dev/ja/problem/support-negative-Array-index/discuss/22

もし興味あれば、 BFE.devでやってみましょう