1
\$\begingroup\$

I am trying to overwrite an object in an array if the title property exists, otherwise just push it to the array. I found two approaches and I wonder which one is the preferred one.

Performance is not really and issue but I wonder whether mutability could be, or just there is a better way to do this altogether.

On this snippet I am using a for loop to edit the original array:

const data = [
  {
    title: 'AAA',
    people: [ 'John', 'Megan',]
  },{
    title: 'BBB',
    people: [ 'Emily', 'Tom']
  }
]

// If inputTitle is not on any of data's title it will append not overwritte
const inputTitle = 'AAA'
const inputPeople = ['Peter', 'Jane']

for (const obj of data) {
  if (obj.title === inputTitle) {
    obj.people = inputPeople
    break
  } else {
    data.push({
      title: inputTitle,
      people: inputPeople
    })
    break
  }
}

console.log(data)

Here I am using high order functions and spread to do the same:

const data = [
  {
    title: 'AAA',
    people: [ 'John', 'Megan',]
  },{
    title: 'BBB',
    people: [ 'Emily', 'Tom']
  }
]

// If inputTitle is not on any of data's title it will append not overwritte
const inputTitle = 'AAA'
const inputPeople = ['Peter', 'Jane']

let res = []

if (data.some(({ title }) => title === inputTitle)) {
  res = data.map(obj => {
    if (obj.title === inputTitle) 
      obj.people = inputPeople
    return obj
  })
} else {
  res = [...data, { title: inputTitle, people: inputPeople}]
}

console.log(res)

In the real task I am reading the data array from a json file with node and writing the changes back to it.

\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

From a short review;

  • The first snippet does not work, try it with inputTitle = 'BBB'

  • You are in essence doing 2 things

    1. Find a possible match
    2. Update the match or insert the data

    Ideally, this is done in 1 'loop'

  • If you don't care too much about performance, I would go for a functional approach

This is my counter-proposal (which modifies the data)

const data = [
  {
    title: 'AAA',
    people: [ 'John', 'Megan',]
  },{
    title: 'BBB',
    people: [ 'Emily', 'Tom']
  }
]

function mergePeople(data, title, people){
  const target = data.filter(record=>record.title===title);
  if(target.length){
    target.forEach(record => record.people = people);
  }else{
    data.push({title, people});
  }
  return data;
}

function quickClone(o){
  return JSON.parse(JSON.stringify(o));
}

console.log(mergePeople(quickClone(data), "AAA", ["Tom", "Jerry"]));
console.log(mergePeople(quickClone(data), "BBB", ["Ben", "Jerry"]));

\$\endgroup\$
4
  • \$\begingroup\$ Thanks, this looks really neat, do I really need to clone the data? \$\endgroup\$
    – Álvaro
    Mar 6, 2021 at 15:29
  • 1
    \$\begingroup\$ Not all, but in order to keep the test clean, I cloned the data. \$\endgroup\$
    – konijn
    Mar 6, 2021 at 15:29
  • \$\begingroup\$ One thing I do not understand is that if target is a brand new array how comes the data array changes when you do the forEach on target? Is that because of the shallow clone of filter not altering the array? \$\endgroup\$
    – Álvaro
    Mar 6, 2021 at 15:53
  • 1
    \$\begingroup\$ target is an array of objects which you can think of as pointers to the objects in data \$\endgroup\$
    – konijn
    Mar 6, 2021 at 15:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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