3

I have nested object, and on incoming change I want to create copy of expenses object and update all timeSpent values. New values are calculated from updated data, so I have to make the calculation for every timeSpent value. I can't find a way to do this.

I have tired creating copy of expenses object using {...expenses.byId} and making array from it with Ovject.values, mapping the data and making calculations. Then problem arises, values are nested, so original object gets changed.

I have tired to make a deep copy of the object, the problem is the same or I'm doing something wrong. If I need to go one level deeper in copyOfExpenses how should I do that?

 const state = {
    expenses: {
        byId: {
            k948zpnp: {
                id: 'k948zpnp',
                category: 'other',
                description: 'book',
                amount: '25',
                timeSpent: '2.5',
                time: '2020-4-21 10:48:10'
            },
            z9e8ipnp: {
                id: 'z9e8ipnp',
                category: 'food',
                description: 'pasta',
                amount: '12',
                timeSpent: '1.2',
                time: '2020-4-21 11:48:10'
            },
        },
        allIds: ['k948zpnp', 'z9e8ipnp']
    }
}

const copyOfExpenses = {
    ...state,
    expenses: {
        ...state.expenses,
        byId: { ...state.expenses.byId },
        allIds: [...state.expenses.allIds]
    }
}

let newInputForTimeSpent = 14
let newData = copyOfExpenses.expenses.allIds.map(
    item =>
        (copyOfExpenses.expenses.byId[item].timeSpent =
            copyOfExpenses.expenses.byId[item].timeSpent * newInputForTimeSpent)
)

console.log(copyOfExpenses) // changes works
console.log(state) // original data gets changed
2
  • 1
    Deep copying objects is non-trivial. Best to use a library function, rather than rolling your own. github.com/terrymorse58/deep-copy-all Commented Apr 22, 2020 at 13:28
  • 1
    If your object is composed only of strings/numbers then the easiest way to deep copy it is to convert it to json and back: copyOfExpenses = JSON.parse(JSON.stringify(state)) Commented Apr 22, 2020 at 14:50

1 Answer 1

2

First approach

I know, hardcoding ids is a terrible idea.

let newInputForTimeSpent = 14;

const copyOfExpenses = {
    ...state,
    expenses: {
        ...state.expenses,
        byId: {
           ...state.expenses.byId,
           k948zpnp: {
             ...state.expenses.k948zpnp,
             timeSpent: state.expenses.k948zpnp.timeSpent * newInputForTimeSpent
           },
           z9e8ipnp: {
             ...state.expenses.z9e8ipnp,
             timeSpent: state.expenses.z9e8ipnp.timeSpent * newInputForTimeSpent 
           }
        },
        allIds: [...state.expenses.allIds]
    }
}

Second approach

const state = {
  expenses: {
    byId: {
      k948zpnp: {
        id: 'k948zpnp',
        category: 'other',
        description: 'book',
        amount: '25',
        timeSpent: '2.5',
        time: '2020-4-21 10:48:10'
      },
      z9e8ipnp: {
        id: 'z9e8ipnp',
        category: 'food',
        description: 'pasta',
        amount: '12',
        timeSpent: '1.2',
        time: '2020-4-21 11:48:10'
      },
    },
    allIds: ['k948zpnp', 'z9e8ipnp']
  }
}

const updateTimeSpentForExpenses = (state, newInputForTimeSpent) => {
  let byId = { ...state.expenses.byId
  }
  const newObjs = Object.entries(byId).map(([key, value]) => {
    return {
      [key]: {
        ...value,
        timeSpent: value.timeSpent * newInputForTimeSpent
      }
    }
  })

  for (let newObj of newObjs) {
    Object.assign(byId, newObj)
  }

  return {
    ...state,
    expenses: {
      ...state.expenses,
      byId,
      allIds: [...state.expenses.allIds]
    }
  }
}

console.log(updateTimeSpentForExpenses(state, 10))

Instead of for of loop with Object.assign you can use Array.reduce

const newObjs = Object.entries(byId).map(([key, value]) => {
      return {
       [key]: {
        ...value,
        timeSpent: value.timeSpent * newInputForTimeSpent
       }
  }
})

for(let newObj of newObjs) {
   Object.assign(byId, newObj)
}

Becomes

byId = Object.entries(byId).map(([key, value]) => {
      return [key, {
        ...value,
        timeSpent: value.timeSpent * newInputForTimeSpent
       }]
  }
}).reduce((acc, ([key, value]) => {
  acc[key] = value;

  return acc;
}, {})
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.