0stone0
0stone0

Reputation: 44093

Javascript spread on array index to 'push' new item to nested array

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)

Upvotes: 2

Views: 1161

Answers (5)

PeterKA
PeterKA

Reputation: 24638

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 );

Upvotes: 0

Andrew Parks
Andrew Parks

Reputation: 8107

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)

Upvotes: 0

RickN
RickN

Reputation: 13500

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.

Upvotes: 0

Unmitigated
Unmitigated

Reputation: 89394

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);

Upvotes: 1

Nina Scholz
Nina Scholz

Reputation: 386680

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; }

Upvotes: 2

Related Questions