как динамически удалять и добавлять значения в массив

#javascript

Вопрос:

У меня есть три кнопки. Все, что мне нужно, это добавить в массив соответствующее значение, нажав на кнопку, но если значение уже существует в моем массиве, мне нужно удалить его из моего массива. Мой код работает только в том случае, если мы используем его дважды (добавляем и удаляем) одну и ту же кнопку. Но если мы используем его с другими — он ломается. Попробуйте это на примере ниже. В чем причина такого поведения ?
Только чистый JavaScript.

 let buttons = document.querySelectorAll('.string')
let array = []
let value = ''

buttons.forEach(el => el.addEventListener('click', () => {
  value = el.innerHTML
  for (i = 0; array.length   1; i  ) {
    if (array[i] === value) {
      delete array[i]
      array = array.filter(x => x) // clear empty values
      console.log('deleted', array)
    } else {
      array.push(value)
      console.log('added', array)
    }
    break
  }
})) 
 <div class="block">
  <div class="string">some</div>
  <div class="string">else</div>
  <div class="string">other</div>
</div> 

Ответ №1:

Это работает не так, как вы ожидаете, потому что, если значение не совпадает с первым элементом массива, оно выталкивается и цикл прерывается. (Ваш цикл всегда прерывается после первого индекса в любом случае…)

Вместо этого вы можете просто использовать Array.prototype.indexOf , чтобы получить индекс элемента, если он существует в массиве, а затем удалить этот индекс, если он существует, или нажать на него, если его нет.

Вы также можете использовать Array.prototype.splice(index, deleteCount) для удаления ненужного индекса вместо выполнения delete и Array.prototype.filter .

 let buttons = document.querySelectorAll('.string')
let array = []
let value = ''

buttons.forEach(el => el.addEventListener('click', () => {
  value = el.innerHTML
  let i = array.indexOf(value);
  if(i > -1)
  {
    array.splice(i, 1);
    console.log('deleted', array)
  }
  else
  {
    array.push(value)
    console.log('added', array)
  }
  
})) 
 <div class="block">
  <div class="string">some</div>
  <div class="string">else</div>
  <div class="string">other</div>
</div> 

Если бы вы хотели использовать вместо этого цикл for, я также исправил ваш исходный фрагмент, чтобы он работал с использованием цикла, который у вас был:

 let buttons = document.querySelectorAll('.string')
let array = []
let value = ''

buttons.forEach(el => el.addEventListener('click', () => {
  value = el.innerHTML
  if(array.length < 1)
  {
    array.push(value)
    console.log("added", array)
    return
  }
  
  for (i = 0; i < array.length; i  ) {
    if (array[i] === value) {
      array.splice(i, 1)
      console.log('deleted', array)
      break
    } else if (i == array.length - 1) {
      array.push(value)
      console.log('added', array)
      break
    }
  }
})) 
 <div class="block">
  <div class="string">some</div>
  <div class="string">else</div>
  <div class="string">other</div>
</div> 

Ответ №2:

1) Вы можете сделать его более эффективным, если используете Set здесь, потому что доступ к значению намного лучше, чем доступ к значению из массива.

2) Лучше использовать textContent вместо innerHTML .

 let buttons = document.querySelectorAll('.string')
let array = [];
const set = new Set();

buttons.forEach(el => el.addEventListener('click', () => {
  const text = el.textContent;
  if (!set.has(text)) {
    set.add(text);
    array.push(text);
  } else {
    const index = array.indexOf(text);
    array.splice(index, 1);
    set.delete(text);
  }
  console.log(array);
})) 
 <div class="block">
  <div class="string">some</div>
  <div class="string">else</div>
  <div class="string">other</div>
</div>