Jennifer Clarke
Jennifer Clarke

Reputation: 13

In Javascript, how do I tell array.filter() to duplicate element objects in the new array?

So simplified code.

var a = [
    { name: "first", num: 1 },
    { name: "first", num: 2 },
    { name: "first", num: 3 },
    { name: "first", num: 4 },
    { name: "first", num: 5 },
    { name: "first", num: 6 },
    { name: "first", num: 7 },
    { name: "first", num: 8 },
    { name: "first", num: 9 }
];

var b = a.filter(function(el) {
    return el.num % 2 == 0;
});

console.log("a1", a); // [1, 20, 3, 40, 5, 60, 7, 80, 9]
console.log("b1", b); // [20, 40, 60, 80]

for (let i = 0; i < b.length; i++) {
    b[i].num = b[i].num * 10;
}

console.log("a2", a); // [1, 20, 3, 40, 5, 60, 7, 80, 9]
console.log("b2", b); // [20, 40, 60, 80]

My new understanding is the array element contains a reference to an object, not the object. What are some ways to get those objects duplicated?

  1. Filter, then build new objects from the filtered array and put the new things in a new array?
  2. Use some method I'm not currently familiar with?
  3. Redesign the code to stop using objects in an array?

Also, what's up with console.log() showing the variables have changed when placed before the for loop?

Upvotes: 1

Views: 92

Answers (5)

fidelio
fidelio

Reputation: 81

A simple solution using some ES6 syntax:

var a = [{name: 'first', num:1}, {name:'first', num: 2}, {name:'first', num: 3}, 
{name:'first', num: 4}, {name:'first', num: 5}, {name:'first', num: 6}, {name:'first', num: 7}, 
{name:'first', num: 8}, {name:'first', num: 9}];

const b = a
.filter(el => {
    if (el.num % 2 === 0) {
        return {
            ...el
        }
    }
})
.map(newEl => newEl.num * 10);

console.log('a', a);  // [1, 2, 3, 4, 5, 6, 7, 8, 9]
console.log('b', b);

  1. .filter() iterates the "a" array and returns only elements with "num" property that reaches the condition. This is a cloned array.
  2. return { ...el } returns a cloned object thanks to spread operator.
  3. .map() creates a new array and returns each "el.num" value * 10

Here some info about .map() .filter() and spread operator:

I found this very interesting site that lists all Javascript functions with their descriptions and shows if is mutable or not, this helps a lot: https://doesitmutate.xyz/

Upvotes: 0

Adrian Brand
Adrian Brand

Reputation: 21658

If you want a new array with the final values you can use reduce to do it all in one go, reduce starts with an accumulator of an empty array and each iteration if it meets the condition it adds a clone with the spread operator overriding the num time 10.

var a = [{name: 'first', num:1}, {name:'first', num: 2}, {name:'first', num: 3}, 
   {name:'first', num: 4}, {name:'first', num: 5}, {name:'first', num: 6}, {name:'first', num: 7}, 
   {name:'first', num: 8}, {name:'first', num: 9}];

const evensTimes10 = array => array.reduce((results, item) => {
  if (item.num % 2 === 0) {
    results.push({ ...item, num: item.num * 10 });
  }
  return results;
}, []);


var b = evensTimes10(a);

console.log('a1',a); // [1, 2, 3, 4, 5, 6, 7, 8, 9]
console.log('b1',b); // [20, 40, 60, 80]

Upvotes: 0

Mister Jojo
Mister Jojo

Reputation: 22365

Yes, elements of Array a are all pointers. so you need to use Object.assign (as many says)

and other solution with array reduce usage (see Adrian Brand comment)

var a = [ { name: 'first', num: 1 } 
        , { name: 'first', num: 2 } 
        , { name: 'first', num: 3 } 
        , { name: 'first', num: 4 } 
        , { name: 'first', num: 5 } 
        , { name: 'first', num: 6 } 
        , { name: 'first', num: 7 } 
        , { name: 'first', num: 8 } 
        , { name: 'first', num: 9 } 
        ] 

var b = a.filter(el=>!(el.num%2)).map(el=>Object.assign({},el))

// other solution with reduce 

var c = a.reduce((acc,cur)=>{
  if (!(cur.num%2) )acc.push(Object.assign({},cur))
  return acc
}, [])

ConsoleArrayNamNum('var a -1-',a)  // [1,2,3,4,5,6,7,8,9]
ConsoleArrayNamNum('var b -1-',b)  // [2, 4, 6, 8]
ConsoleArrayNamNum('var c -1-',c)  // [2, 4, 6, 8]

for(let elm of b)
  { elm.num *= 10 }

ConsoleArrayNamNum('var a -2-',a)  // [1,2,3,4,5,6,7,8,9]
ConsoleArrayNamNum('var b -2-',b)  // [20, 40, 60, 80]


function ConsoleArrayNamNum(title,arr) {
  console.log(title)
  for(let elm of arr)
    { console.log(`{ name: '${elm.name}', num: ${elm.num} }`) }
}
.as-console-wrapper { min-height: 100% !important; }

Upvotes: 0

ChrisG
ChrisG

Reputation: 2948

If you wish to duplicate the objects inside the array, you should use the map function.

var b = a.filter(val => val.num %2 === 0).map(val => Object.assign({}, val, { num: val.num * 10}));

The map function will return a new array with the value returned from the function. In this example, we are creating a new object Object.assign({}) and duplicating the existing object while changing the num field.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map

Upvotes: 1

Adrian Brand
Adrian Brand

Reputation: 21658

If you want to clone objects you will need a clone function, I use this function

const clone = obj =>
  Array.isArray(obj)
    ? obj.map(item => clone(item))
    : obj instanceof Date
    ? new Date(obj.getTime())
    : obj && typeof obj === 'object'
    ? Object.getOwnPropertyNames(obj).reduce((o, prop) => {
        o[prop] = clone(obj[prop]);
        return o;
      }, {})
    : obj;

You can then clone the array with

let c = clone(b);

Which will be a new array where each object is a new clone.

var a = [{name: 'first', num:1}, {name:'first', num: 2}, {name:'first', num: 3}, 
   {name:'first', num: 4}, {name:'first', num: 5}, {name:'first', num: 6}, {name:'first', num: 7}, 
   {name:'first', num: 8}, {name:'first', num: 9}];

var b = a.filter(function(el){return el.num%2==0 });
   
 const clone = obj =>
    Array.isArray(obj)
      ? obj.map(item => clone(item))
      : obj instanceof Date
      ? new Date(obj.getTime())
      : obj && typeof obj === 'object'
      ? Object.getOwnPropertyNames(obj).reduce((o, prop) => {
          o[prop] = clone(obj[prop]);
          return o;
        }, {})
      : obj;

 let c = clone(b);
 
 console.log(b[0] === c[0]);

Upvotes: 1

Related Questions