0

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

NearHuscarl
  • 66,950
  • 18
  • 261
  • 230
TomR
  • 2,696
  • 6
  • 34
  • 87

3 Answers3

1

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);
secan
  • 2,622
  • 1
  • 7
  • 24
1

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
grokked
  • 401
  • 2
  • 8
1

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']);
A_A
  • 1,832
  • 2
  • 11
  • 17
  • 2
    It is not a matter of "want" or "don't want"; state should not be mutated therefore I think you should remove the line `line.description = 'TV'` and keep only the currently commented one (`line = { ...line, description: 'TV' }`). ;) – secan Oct 01 '20 at 09:20
  • 1
    I agree with @secan , updated it. `state2.invoice` still points to the original invoice object though, so updating it in state2 also updates it in state. You could use a library for deep cloning or do it manually – A_A Oct 01 '20 at 09:28
  • 1
    @a-a I am not following you; `state2.invoice` does not refer to the same entity of `state.invoice` as `...state` creates a clone of `state` not a reference to the same object. You would be right if the code were `state2 = { invoice: state.invoice }` but it is not. Am I missing something? – secan Oct 01 '20 at 09:40
  • 2
    The spread operator only generate shallow copies, not deep clones. If the object has nested objects it doesn't clone everything inside it, it just points to it. For instance after the above code you can execute `state2.invoice.id = 12345; console.log(state.invoice.id)` and it will show that the original state has been updated – A_A Oct 01 '20 at 09:45
  • OK, thanks @A_A for deep observation - my intention was to use this construction in the setting of state = {...state, <...>} and not with the state2=... as in my question. I introduced state2 just for expected clarity, but now I see that the confusion arises. I guess - if I use state={...state, <...>} than all is OK with this code. I am just cautious a bit whether map function is good for non-mutation that is required in Redux, but I guess - there is not other solution without map. Find is not working in this setting. – TomR Oct 01 '20 at 09:49
  • 2
    @TomR, `map()` does not modify the original array therefore you should be safe in terms of mutation. @a-a, yes, you are right, I did not consider it; thank you very much for your answer. :) – secan Oct 01 '20 at 09:52