G Josh
G Josh

Reputation: 311

How to update contents of objects inside arrays javascript

I have booksOwned inside an array of objects, and I want to add another book to "John Doe"'s booksOwned.

const [ownerInfo, setOwnerInfo] = React.useState([
  {
    owner: "John Doe",
    booksOwned: ["To Kill a Mockingbird", "1984"],
  },
  {
    owner: "John Doe Jr",
    booksOwned: [
      "Harry Potter and the Philosopher's Stone",
      "The Lord of the Rings",
    ],
  },
])

This is what I've tried so far, does not seem to work.

let data = ownerInfo
let newBook = "Pride and Prejudice"

//to store currently owned books
let currBooks = []

//get the index of object I need
let ind = data.map((val, i) => {
  if (val.owner === "John Doe") {
    currBooks = val.booksOwned
    return i
  } else {
    return -1
  }
})

let bookIndex = currBooks.indexOf(newBook)

//insert only if book does not yet exist
if (bookIndex === -1) {
  data[ind] = {
    owner: "John Doe",
    booksOwned: currBooks.push(newBook),
  }
  setOwnerInfo(data)
}

Upvotes: 0

Views: 90

Answers (2)

Reel
Reel

Reputation: 364

A simple approach would be to use functional setState. Basically, setState can accept function as it's argument. Something like this:

const newBook = 'Pride and Prejudice'

// When using setOwnerInfo with functions as argument, it
// receives currentState as argument so we can use it.

// Use map to create a new array, instead of modifying it.
setOwnerInfo(owners => owners.map(owner => {
  // First check for correct owner. Return if this is wrong one.
  if (owner.onwer !== "John Doe") { return owner }

  // Second, check that book doesn't exist already. Return if it does.
  if (owner.booksOwned.find(ownedBook => ownedBook === newBook) !== undefined) { return owner }

  // Book doesn't exist. Add it.
  // Let's create a new object, instead of mutating it.
  return {...owner, booksOwned: [...owner.booksOwner, newBook]}
}))

One thing about react state is that you shouldn't mutate it. It can lead to many bugs. It's better to just copy it and save yourself a hassle in the future.

In case you want to filter a book away, you can just change it like this:

setOwnerInfo(owners => owners.map(owner => {
  // First check for correct owner. Return if this is wrong one.
  if (owner.onwer !== "John Doe") { return owner }

  // Filter here
  // Let's create a new object, instead of mutating it.
  return {...owner, booksOwned: owner.booksOwned.filter(ownedBook => ownedBook !== newBook)}
}))

This should remove all array items that match newBook (even if somehow there are duplicates, which shouldn't happen since we're guarding against duplicates above).

Upvotes: 2

Fletcher Rippon
Fletcher Rippon

Reputation: 1975

Here is an easy way to do this in ES6 Javascript:

// owner set to name of the owner
// newBook set to book title
const addBook = (owner, newBook) => {
  // Find owner by name
  const person = ownerInfo.find(person => person.owner === owner)
  // if book already owned escape function
  person.booksOwned.find(exists => { if(exists == newBook) return })
  // Add book to owned books
  person.booksOwned.push(newBook)
}

// example on how to use
addBook("John Doe", "New Book")

Or if you want to use it with setState:

const addBook = (owner, newBook) => {
  const person = ownerInfo.find(person => person.owner === owner)
  const newOwnerInfo = person.booksOwned.push(newBook)
  person.booksOwned.find(exists => { if(exists == newBook) return })
  setOwnerInfo(newOwnerInfo) // to change state
}

This function can be used for any of the owners.

Hope this helps.

Upvotes: 1

Related Questions