0

I don't like using map and looping to create a new array from an old array in JavaScript when just one item changes. I have a simple array that has data like this:

const speakerData = [{name: 'joe',id: 101,favorite: true},
                     {name: 'sam',id: 102,favorite: false},
                     {name: 'jon',id: 103,favorite: false}]

And I want to update record 102 to favorite is true (toggle favorite).

I have the following code that works:

const speakerId = 102;
const newSpeakerData = speakerData.map((rec) => {
  if (rec.id === speakerId) {
    rec.favorite = !rec.favorite;
    return rec;
  } else {
    return rec;
  }
});

I want something like what I have below but it obviously does not work.

const speakerRec = speakerData.find(a=>a.id === speakerId);
speakerRec.favorite = !speakerRec.favorite;
const newSpeakerData = [{...speakerData},speakerRec]

Is there a clever one line I can make this happen with ES7?

Here is the answer I was looking for that @adiga put in the comments below.

const speakerId = parseInt(e.target.attributes['data-sessionid'].value);
const index = speakerData.findIndex((a) => a.id === speakerId);
const newSpeakerData = Object.assign([...speakerData], {
  [index]: { ...speakerData[index], favorite: !speakerData[index].favorite }
});
18
  • Intead of find, use findIndex like: const newSpeakerData = [...speakerData]; const index = speakerData.findIndex(a=>a.id === speakerId); newSpeakerData[index] = <immutably update index>; setState(..)
    – adiga
    Commented Dec 24, 2019 at 16:12
  • <immutably update index> is the part I'm having trouble with
    – Pete
    Commented Dec 24, 2019 at 16:43
  • 1
    @Victor Did you upvote to outweigh the downvote on the question, or because (hover over the upvote button) "This question shows research effort; it is useful and clear"? Your "or i'm understanding wrong?" suggests that you don't really find it crystal clear. Upvoting to balance out a downvote is highly inappropriate.
    – Ivar
    Commented Dec 24, 2019 at 18:03
  • 1
    "but it obviously does not work" What doesn't work @Pete? The first two lines of what you show seem to do exactly what you are asking for.
    – Ivar
    Commented Dec 24, 2019 at 18:14
  • 1
    I'm looking for a more language rich solution than using find or findrec. If it's not possible to use the spread operator I understand that but I've seen other examples where it seems possible using syntax similar to my suggestion of [...,newrec]. Thank you to whoever re-opened this as well as whoever upvoted. I upvote all the time and I don't believe you have to have a appropriate reason.
    – Pete
    Commented Dec 24, 2019 at 19:47

2 Answers 2

0

One option is, while mapping, also put favorite: id === speakerId ? !favorite : favorite into the returned object:

const speakerData = [{
    name: 'joe',
    id: 101,
    favorite: true
  },
  {
    name: 'sam',
    id: 102,
    favorite: false
  },
  {
    name: 'jon',
    id: 103,
    favorite: false
  }
];
const speakerId = 102;
const mappedData = speakerData.map(({ favorite, name, id }) => ({
  name,
  id,
  favorite: id === speakerId ? !favorite : favorite
}));
console.log(mappedData);

Doesn't look that pretty, but that's what I'd prefer, and I'm doubtful there's a better way.

Or, you can squish it onto one line if you really have to (but you probably shouldn't, it's too hard to read):

const mappedData = speakerData.map(({ favorite, name, id }) => ({ name, id, favorite: id === speakerId ? !favorite : favorite }));
0

You pretty much need the map for keeping the array immutable, but you might get away with a neater syntax by using the conditional operator instead of if/else statements:

const speakerId = 102;
const newSpeakerData = speakerData.map((rec) =>
  rec.id === speakerId
    ? {...rec, favorite: !rec.favorite}
    : rec
);

(The spread syntax usage also makes sure not to mutate the rec object).

If this is still too long or has too much indentation, you can of course write a helper function instead to use like

const matchSpeakerId = rec => rec.id === 102;
const newSpeakerData = mapWhere(speakerData, matchSpeakerId, rec => ({...rec, favorite: !rec.favorite}));

Not the answer you're looking for? Browse other questions tagged or ask your own question.