How to use JavaScript spread… syntax to change the one field of the object, that belongs to the array and is accessed by name-value pair?

Here is the code (it fails to compile at the sentence that builds the state2, i.e. at the second spread):

let line_id = 6;

let state = {
  invoice: {
    id: 1015,
    description: 'web order',
  },
  lines: [
    {id: 5, description: 'phone', color: 'black'},
    {id: 6, description: 'tablet', color: 'blue'},
    {id: 7, description: 'computer', color: 'gray'},
  ]
};

//this alert and this access pattern works, so, I would like to use
//.find... to access element in spread... structure as well
//alert(state['lines'].find(line=>line['id']==line_id)['description']);

let state2 = {
   ...state,
   ['lines']: { ...state['lines'],
      find(line=>line['id']==line_id): { ...state['lines'].find(line=>line['id']==line_id),
      ['description']: 'TV',
      },
   },
};

alert(state2['lines'].find(line=>line['id']==line_id)['description']);

I have state structure, I access lines array, I access the specific line by name-value pair id=6 and I would like to change the value of the field description. This effort is the continuation of https://stackoverflow.com/a/64116308/1375882 in which I am trying to create the general procedure, that use the spread… syntax and the access-by-name strategy for updating the complex object/array tree. In fact – this complex tree is the state of the Redux reducer and that update happend in the action that process the valueSetter function of the AgGrid. But – this is generally the interesting exercise by itself to better understand spread… and JavaScript and JSON structure in JavaScript.

So – the only question is: how to write line

find(line=>line['id']==line_id): { ...state['lines'].find(line=>line['id']==line_id),

so that the code compiles? How can I access the certain element of the array by name-value pair in this setting:

Note, that I am trying to build general code:

find(line=>line[keyFieldName]==keyFieldValue): { ...state['lines'].find(line=>line[keyFieldName]==keyFieldValue),

that uses arbitrary field names and field values – so that such handler can update the any field of the any record of arbitrary 2D AgGrid in React/Redux setting.

The desired result of my code: 1) it should compile; 2) the second alert should return ‘TV’.

Answer

If I understood correctly what you want to achieve, this should work:

let line_id = 6;

let state = {
  invoice: {
    id: 1015,
    description: 'web order',
  },
  lines: [{
      id: 5,
      description: 'phone',
      color: 'black'
    },
    {
      id: 6,
      description: 'tablet',
      color: 'blue'
    },
    {
      id: 7,
      description: 'computer',
      color: 'gray'
    },
  ]
};

const stateKeyId = 'lines';
const itemKeyId = 'id';
const itemAttr = 'description'

let state2 = {
  ...state,
  [stateKeyId]: state[stateKeyId].map(item => {
    if (item[itemKeyId] == line_id) {
      return ({
        ...item,
        [itemAttr]: 'TV'
      });
    }
    return item
  })
}

console.log(state2);