javascript: изменить поле в объекте массива одной строкой

Рейтинг: 0Ответов: 3Опубликовано: 26.03.2023

есть массив объектов (ассоциативных массивов):

data = [
    {
        name: 'obj1',
        value: data1
    },
    {
        name: 'obj2',
        value: data2
    },
]

можно ли в 1 строку заменить значение value в объекте с заданным name и вернуть измененный массив?

это для изменения состояний через setState в компоненте React нужно

можно конечно все в несколько строчек сделать через создание временного объекта

const tmp = JSON.parse(JSON.stringify(this.state.data));

const index = this.state.data.findIndex(el => el.name === name));
tmp[index]['value'] = newValue;

setState({
    data: tmp
});

но хотелось бы в 1 строку, чтоб не писать лишнего кода

Ответы

▲ 1Принят

setState может принимать как объект так и функцию, по Вашему коду правда не понятно хук это или нет (похоже что нет), но насколько я помню с хуками так же. Я бы не гнался за краткостью в угоду читабельности

setState((oldState) =>  {
    const index = oldState.data.findIndex(el => el.name === name);
    oldState.data.splice(index, 1, {...oldState.data[index], value: newValue }) 
    return { data: Array.from(oldState.data)}
});

Теоретически это можно записать так (но я бы не стал):

this.state.data.splice(this.state.data.findIndex(el => el.name === name), 1, {...this.state.data[this.state.data.findIndex(el => el.name === name)], value: newValue}) 

setState({
   data: Array.from(this.state.data)
});

splice преобразовывает массив по месту и возвращает удаленные элементы поэтому его нельзя куда-то засунуть, он должен быть отдельной строкой.

Обновление

Я все таки смог извратиться и засунуть все в одну строку, но оно Вам надо?) Основной смысл вырезать до индекса и после и втиснуть между измененный элемент:

setState({data:  Array.from([...this.state.data.slice(0, this.state.data.findIndex(el => el.name === name)), {...this.state.data[this.state.data.findIndex(el => el.name === name)], value: newValue}, ...this.state.data.slice(this.state.data.findIndex(el => el.name === name) +1, this.state.data.length)])})
▲ 2

Достаточно воспользоваться методом .map который сразу возвращает новый массив.

setState({
    data: this.state.data.map(el => el.name === name ? {...el, value: newValue} : el)
});
▲ -1

как вариант нормалное решение

  const [state, setState] = useState([
    {
      name: 'obj1',
      value: 1,
    },
    {
      name: 'obj2',
      value: 2,
    },
  ]);

  useEffect(() => {
    const name = 'obj2';
    setState((prev) => {
      const { foundObj, filteredArr } = prev.reduce(
        (acc: { foundObj; filteredArr }, next) => {
          if (next.name === name) {
            acc.foundObj = { ...next };
          } else {
            acc.filteredArr.push(next);
          }
          return acc;
        },
        { foundObj = {}, filteredArr: [] },
      );
      return [
        ...filteredArr,
        {
          ...foundObj,
          value: 3333,
        },
      ];
    });
  }, []);

в одну строку

  useEffect(() => {
    const name = 'obj2';
    // const reg = new RegExp(String.raw`{("name":${name}, "value")\w+}`);
    // setState((prev) => JSON.parse(JSON.stringify(prev).replace(reg, '$1:3333')));

    const reg = new RegExp(String.raw`{(\w+:${name}\w+(?=:))\w+}`);
    setState((prev) => JSON.parse(JSON.stringify(prev).replace(reg, '$1:3333')));
  }, []);