2

Consider the following data:

let data = [
    { foo: true, bar: [ 1, 2, 3 ] },
    { foo: true, bar: [ 8, 9,   ] }
];

I'm trying to push something to the nested bar array on index 1 using the spread syntax (...).

So the final array should become:

[
    { foo: true, bar: [ 1, 2, 3 ] },
    { foo: true, bar: [ 8, 9, 'new-item'  ] }
]

Normally, we'll just use push: data[1].bar.push(0), but I need a spread solution


I've tried to use this approach:
How to push new elements to a nested array of objects in JavaScript using spread syntax

data = [ ...data, {[1]: { ...data[1], bar: [ ...data[1].bar, 'new-item' ] } }] 

But this will append another object with a single key 1, it does not alter data[1].


Then, I've tried to use Object.assign() but again ended up with a new index:
Replace array entry with spread syntax in one line of code?

data = [ ...data, Object.assign({}, data[1], { bar }})

tl;dr, How do I append something to an array, part of an object, that's inside an array of objects, using the spread syntax?

Please link me to a duplicate, or provide a way to do this


Playground:

let data = [
    { foo: true, bar: [ 1, 2, 3 ] },
    { foo: true, bar: [ 8, 9 ] }
];

// 'Regular' push method
// data[1].bar.push(0);

// Using spread reassign
// data = [ ...data, {[1]: { ...data[1], bar: [ ...data[1].bar, 'new-item' ] } }] 

// Using Object.assign
data = [ ...data, Object.assign({}, data[1], {bar: [ 'new-item' ] } ) ];

console.log(data)

4
  • Im not posting an answer, but that seems to work : data = [ data[0], { ...data[1], bar: [ ...data[1].bar, 'new-item' ] } ]
    – Foobar
    Commented Jan 19, 2023 at 16:19
  • This works fine @Foobar, but how would you re-write this if data has 100 items and you need to change index 42?
    – 0stone0
    Commented Jan 19, 2023 at 16:26
  • @0stone0 @Foobar's answer is close to my answer: simply replace the static ....data[1] with a ...data.slice(0, desired_index_to_edit) (& follow with a ...data.slice(desired_index_to_edit+1))
    – RickN
    Commented Jan 19, 2023 at 16:27
  • May you work only on one item, and dont spread everthing: data[1] = { ...data[1], bar: [ ...data[1].bar, 'new-item' ] } So 1 can now easy be 42.
    – Foobar
    Commented Jan 19, 2023 at 16:29

5 Answers 5

2

You could take an outer Object.assign with an array as target and an object with the index as key.

let
    data = [
        { foo: true, bar: [1, 2, 3] },
        { foo: true, bar: [8, 9] }
    ];

data = Object.assign(
    [...data],                            // target array
    { 1: {                                // array index as key
        ...data[1],
        bar: [...data[1].bar, 'new-item']
    } }
);

console.log(data);
.as-console-wrapper { max-height: 100% !important; top: 0; }

2
  • I wonder. why [...data] and not just data?
    – Foobar
    Commented Jan 19, 2023 at 16:53
  • @Foobar, you could do that, but i would avoid to mutate the data. Commented Jan 19, 2023 at 17:03
1

You can use Object.assign on the array itself.

let data = [
    { foo: true, bar: [ 1, 2, 3 ] },
    { foo: true, bar: [ 8, 9,   ] }
];
data = Object.assign([...data], {1: { ...data[1], bar: [...data[1].bar, 'new-item']}});
console.log(data);

1
  • Thanks, +1. I'm sorry that Nina was 42 seconds faster. Will accept her answer if there are no shorter alternatives.
    – 0stone0
    Commented Jan 19, 2023 at 16:24
0

I don't think this is more readable than many other options open to you but this satisfies the requirement of "using object spread".

let data = [
  { foo: true, bar: [ 1, 2, 3 ] },
  { foo: true, bar: [ 8, 9,   ] },
  { foo: false, bar: [ ] }
];

let idx = 1;
let newData = [ 
  ...data.slice(0, idx), 
  { 
    ...data[idx], 
    bar: [ ...data[idx].bar, 'new-item' ]
  },
  ...data.slice(idx+1)
];

console.log(newData);

This will first take your data and cut the array up to the item you wish to replace (index 1). A new object follows, then the rest of the array (if any) follows.

2
  • Thanks, this is decent, but dont really like the 2 slice. The object assign seems like a more clean way of doing this.
    – 0stone0
    Commented Jan 19, 2023 at 16:25
  • Unless my benchmark is wrong, slicing twice like this is faster than the Object.assign(), though.
    – RickN
    Commented Jan 19, 2023 at 16:30
0

First, treat the array like an object with numbered keys, and use the spread operator and override the necessary index. Then, use Object.values() to treat it like an array.

let data = [
  { foo: true, bar: [ 1, 2, 3 ] },
  { foo: true, bar: [ 8, 9, ] }
]

data = Object.values({ ...data, 1: { ...data[1], bar: [ ...data[1].bar, 'new-item' ] } })

console.log(data)

In this particular case, if the index you need to alter is near the beginning of the array, you can also use an IIFE to allow for a destructuring approach, like this:

let data = [
  { foo: true, bar: [ 1, 2, 3 ] },
  { foo: true, bar: [ 8, 9, ] }
]

data = (([first, {bar, ...rest}]) => [first, {...rest, bar:[...bar, 'new-item']}])(data)

console.log(data)

0

You can just assign the first element index to the modified element that uses Array#map and the spread operator as follows:

const data = [
    { foo: true, bar: [ 1, 2, 3 ] },
    { foo: true, bar: [ 8, 9,   ] }
];

data[1] = [data[1]].map(({foo,bar}) => ({foo,bar:[...bar,"some new value"]}))[0];

console.log( data );

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