Массив JS уменьшает и обновляет соседние реплики

#javascript #arrays #duplicates

Вопрос:

Входные данные представляют собой массив, содержащий строки трех типов: буква L с присоединенной цифрой, буква R с присоединенной цифрой и буква D с присоединенной цифрой. Эти строки могут повторяться. Задача состоит в том, чтобы написать функцию, которая возьмет соседние Ds и уменьшит их до одного D с обновленной цифрой Ds, которые присутствовали раньше. Обратите внимание, что только соседние Ds должны быть уменьшены и подсчитаны. Примеры выходных данных, которые я хочу:

 input: ['D1', 'D1', 'L1', 'D1', 'D1', 'D1', 'R1']
output: ['D2', 'L1', 'D3', 'R1']

input: ['R1', 'D1', 'L1', 'D1', 'D1', 'D1', 'D1', 'L1', 'R1', 'D1', 'D1', 'D1']
output: ['R1', 'D1', 'L1', 'D4', 'L1', 'R1', 'D3']
 

По-видимому, моя функция не работает по двум основным причинам: сращивание массива во время цикла (я пытаюсь избавиться от избыточного Ds), и я заставляю функцию выходить за рамки массива на последней итерации (arr[i 1].charAt(0) === ‘D’). Я не могу преодолеть эти проблемы.

 function reduceD(arr) {
    for (i = 0; i < arr.length; i  ) {
            let str = arr[i];
            if (arr[i].charAt(0) === 'D' amp;amp; arr[i 1].charAt(0) === 'D') {
              arr[i] = 'D'   (Number(str[1])   1);
              arr.splice(i 1, 1); 
          }
        }
    return arr;
}
 

Ответ №1:

Пожалуйста, используйте функцию Array.reduce.

 function reduceD(arr) {
    const result = arr.reduce((total, val) => {
        if(total.length === 0) 
            total.push(val);
        else {
            const last = total[total.length - 1];
            const newValue = last.charAt(0)   (Number(last.slice(1))   1);
            last.charAt(0) === val.charAt(0) ? total.splice(total.length - 1, 1, newValue) : total.push(val);
        }
        return total;
    }, []);
    return resu<
}

console.log(reduceD(['D1', 'D1', 'L1', 'D1', 'D1', 'D1', 'R1']));
console.log(reduceD(['R1', 'D1', 'L1', 'D1', 'D1', 'D1', 'D1', 'L1', 'R1', 'D1', 'D1', 'D1'])); 

Ответ №2:

Чтобы добавить к другому ответу: Если вы хотите выполнить итерацию по массиву при удалении элементов, сделайте это в обратном порядке, чтобы не пропустить ни одного элемента. Вот исправленная версия вашего кода и одна с простым циклом, в котором IMHO легче понять (и намного проще, чем Array.reduce функция в другом ответе для начинающих)

Я согласен с другим ответом, хотя, это Array.reduce путь для более опытных программистов, которые знают, как Array.reduce работает.

 // the fixed version of your code
function reduceD(arr) {
  // iterate backwards to make the splice possible
  // also note how the loop does never reach i == 0 but stops at 1 instead
  for (let i = arr.length - 1; i > 0; i--) {
    let str = arr[i];
    if (arr[i].charAt(0) === 'D' amp;amp; arr[i - 1].charAt(0) === 'D') {
      arr[i - 1] = 'D'   (Number(str[1])   1);
      arr.splice(i, 1);
    }
  }
  return arr;
}

// the IMHO simpler way of creating a new array
function buildingNewArray(arr) {
  let out = [];
  let dsFound = 0;
  for (const elem of arr) {
    if (elem.charAt(0) === 'D') {
      dsFound  ;
    } else {
      // there is no D in the current element
      if (dsFound > 0) {
        // add all found Ds as one
        out.push('D'   dsFound);
        dsFound = 0;
      }
      // always output the current element to the output
      out.push(elem);
    }
  }
  // we're done with the loop but there might be some Ds to output
  if (dsFound > 0) {
    out.push('D'   dsFound);
  }

  return out;
}


const arr1 = ['D1', 'D1', 'L1', 'D1', 'D1', 'D1', 'R1'];
const arr2 = ['R1', 'D1', 'L1', 'D1', 'D1', 'D1', 'D1', 'L1', 'R1', 'D1', 'D1', 'D1'];
console.log(buildingNewArray(arr1));
console.log(buildingNewArray(arr2));
console.log(reduceD(arr1));
console.log(reduceD(arr2)); 

Комментарии:

1. Спасибо вам, так круто и умно, как вы исправили мой код с помощью обратной итерации. Тем легче мне это понять, так как я новичок. Большое спасибо!

2. Ваше второе решение buildingNewArray Мне тоже нравится, я некоторое время смотрел на него, чтобы, наконец, понять логику, лежащую в его основе. То, что я пропустил раньше, — это трюк, чтобы вернуть счетчик Ds к нулю после его перемещения в новый массив, как вы сделали с dsFound = 0 внутри цикла. Мило!

Ответ №3:

Уменьшите массив до нового массива, если текущий элемент не является правильным символом или последний элемент в накопителе ( acc ) не начинается с правильного символа, нажмите текущий элемент. В противном случае увеличьте номер последнего элемента.

 const isChar = (char, item) => !!item?.startsWith(char);
const incItem = item => `${item[0]}${ item.substring(1)   1}`;

const reduceD = (char, arr) => 
  arr.reduce((acc, c) => {
    if(!isChar(char, c) || !isChar(char, acc[acc.length - 1])) acc.push(c); 
    else acc[acc.length - 1] = incItem(acc[acc.length - 1]);
  
    return acc;
  }, []);

console.log(reduceD('D', ['D1', 'D1', 'L1', 'D1', 'D1', 'D1', 'R1'])); // ['D2', 'L1', 'D3', 'R1']

console.log(reduceD('D', ['R1', 'D1', 'L1', 'D1', 'D1', 'D1', 'D1', 'L1', 'R1', 'D1', 'D1', 'D1'])); // ['R1', 'D1', 'L1', 'D4', 'L1', 'R1', 'D3']