javascript: изменить поле в объекте массива одной строкой
есть массив объектов (ассоциативных массивов):
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 строку, чтоб не писать лишнего кода
Ответы (3 шт):
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)])})
как вариант нормалное решение
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')));
}, []);
Достаточно воспользоваться методом .map который сразу возвращает новый массив.
setState({
data: this.state.data.map(el => el.name === name ? {...el, value: newValue} : el)
});