1

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?

3
  • The browser console shows the variables in their current state, not the state they were in when they were logged. If you console.log('a1',JSON.stringify(a)); you should see a difference. Commented Sep 23, 2019 at 22:46
  • Use map, not filter, to create new objects (copies) in a new array. Commented Sep 23, 2019 at 22:50
  • Like const c = b.map(({name, num}) => ({name, num: num*10})); Commented Sep 23, 2019 at 22:51

5 Answers 5

1

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

Sign up to request clarification or add additional context in comments.

6 Comments

Writing name: val.name in the object literal is probably easier than the Object.assign though :-)
This only works for objects 1 deep, if any of the properties are objects then you will need to deep clone.
@Bergi, yes but if you add more properties to the objects, you won't need to worry about it this way. Also I updated wording. Thank you
@Adrian Brand, that is correct, however with the use case presented, it did not seem necessary.
@AdrianBrand It's fine because only the num property on the first level changes in the new object.
|
1

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

Comments

0

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

4 Comments

This is not an answer to how to not have the same reference in the final array.
@AdrianBrand -> my bad (or poor english; no more important after Brexit)
A filter followed by a map is best done with a reduce as it can be done in a single iteration, if item meets condition add new object to results.
@AdrianBrand, that's right; I added this one, thanks!
0

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]

Comments

0

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/

1 Comment

My fault. Links now point to English documentation. I hope it helps.

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.