0

I am attempting to modify an array of objects and I use a map inside a for loop. My implementation always seem to return the last item in the map.

Code:

let testArray = [
    {
        body: 'test sentence',
        uid: "a"
    },
    {
        body: 'Another test sentence',
        uid: "b"
    }
]

var allItems = []
for (var item of testArray) {
    let splittedBody = item.body.split(' ')

    let splittedArray = splittedBody.map((word, i) => {
        item.body = word
        item.objectID = `${item.uid}:${i}`
        return item
    })
    allItems.push(splittedArray)
}

allItems = Array.prototype.concat(...allItems)
console.log(allItems)

The above code prints the following:

[ { body: 'sentence', uid: 'a', objectID: 'a:1' },
  { body: 'sentence', uid: 'a', objectID: 'a:1' },
  { body: 'sentence', uid: 'b', objectID: 'b:2' },
  { body: 'sentence', uid: 'b', objectID: 'b:2' },
  { body: 'sentence', uid: 'b', objectID: 'b:2' } ]

What I want is:

[ { body: 'test', uid: 'a', objectID: 'a:0' },
  { body: 'sentence', uid: 'a', objectID: 'a:1' },
  { body: 'Another', uid: 'b', objectID: 'b:0' },
  { body: 'test', uid: 'b', objectID: 'b:1' },
  { body: 'sentence', uid: 'b', objectID: 'b:2' } ]

EDIT: Ideally, I would not like to construct another item in my map function because each object in the testArray may have attributes that do not exist in other object. Constructing another item in the map function would mean that I have to check for undefined in those attributes and this is very prone to errors.

Koh
  • 2,687
  • 1
  • 22
  • 62

3 Answers3

2

Your problem here come from the shadowing that you do with item:

let testArray = [{
    body: 'test sentence',
    uid: "a"
  },
  {
    body: 'Another test sentence',
    uid: "b"
  }
]

var allItems = []
for (var item of testArray) {
  let splittedBody = item.body.split(' ')

  let splittedArray = splittedBody.map((word, i) => {

    // Create a clone of item -> don't use item
    var itemClone = JSON.parse(JSON.stringify(item)); 
    
    itemClone.body = word
    itemClone.objectID = `${item.uid}:${i}`
    return itemClone       // <-- return the modified clone
  })
  allItems.push(splittedArray)
}

allItems = Array.prototype.concat(...allItems)
console.log(allItems)

item was declared in the for loop:

for (var item of testArray) {

But then you actually modified the body and the objectID from it:

    let splittedArray = splittedBody.map((word, i) => {
        item.body = word
        item.objectID = `${item.uid}:${i}`
johannchopin
  • 13,720
  • 10
  • 55
  • 101
  • Ideally, I do not want to construct a new item to append to `allItems` because each object in the json can have differing attributes. Each object has up to 10 attributes and constructing a new item means that I have to check for `undefined` in all the differing attributes. Is there a way to do so? – Koh Feb 11 '20 at 12:53
  • @Koh Yes you can do that using a clone of the initial `item`. Take a look at [this question](https://stackoverflow.com/questions/728360/how-do-i-correctly-clone-a-javascript-object) for more details. I updated my example. – johannchopin Feb 11 '20 at 13:00
0

When you set item.body, it mutates the original object. And you need more objects than the length of the testArray since you create new objects by splitting.

Update:

Since you want existing properties of item to remain, you can use spread operator as following:

let testArray = [{
    body: "test sentence",
    uid: "a",
    foo: 'foo'
  },
  {
    body: "Another test sentence",
    uid: "b",
    bar: 'bar'
  }
];

var allItems = []
for (var item of testArray) {
  allItems.push(
    item.body.split(' ').map((word, i) => ({
        ...item,
//    ^^^^^^^ Here you can use spread operator to merge newly created object with the item

        body: word,
        objectID: `${item.uid}:${i}`
      })
    )
  )
}

allItems = Array.prototype.concat(...allItems)
console.log(allItems)
Harun Yilmaz
  • 8,281
  • 3
  • 24
  • 35
0

You can make it like this simple.

let testArray = [
{
    body: 'test sentence',
    uid: "a"
},
{
    body: 'Another test sentence',
    uid: "b"
}
]
var allItems = [];
    for (var item of testArray) {
      let splittedBody = item.body.split(' ')
      splittedBody.map((word, i) => {
        let item1: any = {};
        item1.body = word;
        item1.uid = item.uid;
        item1.objectID = `${item.uid}:${i}`;
        allItems.push(item1);
      });
    }
    allItems = Array.prototype.concat(...allItems);
    console.log(allItems);
Mihir Patel
  • 416
  • 5
  • 13