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'.

3

There are 3 best solutions below

0
On BEST 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);

0
On

find(line=>line['id']==line_id) should become [find(line=>line['id']==line_id)], since just like the string it must be between square brackets for js to work properly.

Also, if you are using find from lodash, it will return the object, therefore if you need to use the id as key you can do something like:

[get(find(line => line['id'] === line_id]), 'id')]: whatever

a few observations though:

  • always please always use === over == in js
  • avoid snake_case, use camelCase with js, since it's standard
  • your code is not actually handling missing items correclty, if you need to do so split it in multiple lines since it would be more comprehensible
6
On

You can use the map method from arrays to return different elements based on the original one.

Here's how you could use it:

line_id = 6;

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'},
  ]
};

state2 = {
   ...state,
   lines: state.lines.map(line => {
     if (line.id === line_id)
       return { ...line, description: 'YT' }
     return { ...line }
   })
};

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