4

Seemed quite intuitive to me, but turns out that's not how things work! The goal is to remove the passed element if it exists and return the rest. I know there's a number of ways of achieving this - including filter: const rest = selection.filter(i => i !== item) - but, as I said, thought this approach would be a thing - as it is for objects/key:value pairs.

if (selection.includes(item)) {
  // remove if available
  const [item, ...rest] = selection;
  
  setSelection(rest)
} else {
  // ...
}

The way destructuring works is it assigns the first element of selection to item and assigns the rest of the items to rest - an array. Which is correct - that's how things work, from my understanding at least.

Is it possible to extract a specific item from an array and unpack the rest of the elements to a variable by using destructuring assignment ?

Gass
  • 7,536
  • 3
  • 37
  • 41
kawerewagaba
  • 1,107
  • 2
  • 15
  • 21
  • 1
    Use filter. What you are trying to do with destructuring is not possible. – Omkar76 Jan 06 '21 at 06:34
  • 1
    you could do an ugly hack: https://jsfiddle.net/tfca7ow9/, but don't use that :P – Nick Parsons Jan 06 '21 at 06:38
  • Turns out it's possible, as shown by @NickParsons and Brett Zamir - downside being it's more work - at least with the current implementation. – kawerewagaba Jan 06 '21 at 08:03
  • @Omkar76 I definitely used filter - just thought there could be a more "elegant" way. I also didn't want to conclude with `not possible` before I heard from the community – kawerewagaba Jan 06 '21 at 08:06
  • 1
    @colinwagaba, I get your point. I didn't think about destructuring array to an object. I know I should be more careful saying *not possible* next time =) – Omkar76 Jan 06 '21 at 09:00

2 Answers2

4

Here's a strange way it could be done:

const item = 5
const selection = [3, 4, 5, 6]
const itemPos = selection.indexOf(item)

if (selection.includes(item)) {
  // remove if available
  const {[itemPos]: item, ...rest} = selection

  // `rest` is now an object, so convert back to an array:
  console.log(Object.values(rest)) // [3, 4, 6]
  setSelection(Object.values(rest))
} else {
  // ...
}

Since I used const in both places, the second item is a different one, but one could remove const and wrap the expression in parentheses to reassign item (as I think you had originally requested):

let rest, item = 5
const selection = [3, 4, 5, 6]
const itemPos = selection.indexOf(item)

if (selection.includes(item)) {
  // remove if available
  ({[itemPos]: item, ...rest} = selection)
  console.log(Object.values(rest)) // [3, 4, 6]
  setSelection(Object.values(rest))
} else {
  // ...
}

But if you wanted to follow a similar pattern without using filter, you could use splice:

const item = 5
const selection = [3, 4, 5, 6]
const itemPos = selection.indexOf(item)

if (selection.includes(item)) {
  // remove if available
  const rest = [...selection] // Copy array if you still need `selection`; otherwise, we could just splice and pass `selection`
  rest.splice(itemPos, 1)
  console.log(rest)
  setSelection(rest)
} else {
  // ...
}

None of these are especially appealing compared to filter, but it should show how the approaches would work.

Brett Zamir
  • 14,034
  • 6
  • 54
  • 77
0

First, we need to be aware that arrays in JS can be treated as objects.

As you can see here:

console.log(typeof [0,1,2])

Okay, so... now if we read the syntax of destructuring assignments we can see that there is the possibility to extract an specific element from an object by using its key.

const { [key]: a } = obj

For example:

const arr = ["", "", ""]

const { [0]: a } = arr

console.log(a)

The gets extracted and assigned to a by using 0 as the key, which is the index of this item in the array.

... by combining the previews syntax with the following, it is possible to unpack the rest of the elements of the array to a variable

const { a, b, ...rest } = obj

const arr = ["", "", ""];

const {[2]: a, ...rest} = arr

console.log(Object.values(rest))

Due to the array being treated as an object it's necessary to use Object.values() to convert the rest object, with the unpacked elements, back to an array.

In case your variables have already been declared, wrap the variable names in brackets so that the JS can parse correctly:

let a, rest, arr = ["", "", ""];

({[1]: a, ...rest} = arr)

console.log(Object.values(rest))
Gass
  • 7,536
  • 3
  • 37
  • 41