0

I am scratching my head on making assignments to 2 dim object array by re-using previously set objects (Angular/Typescript). My result shows that the last assignment overrides the previous two and I cannot figure out why. Can you please have a look at what I am missing?

    export interface Criteria {
        fieldValue: any;
        fieldName: string;
        childItems?: Criteria[];
    }


                        // function to build my array of objects:
    setCriteria() {
                       // parent object 1 
                       // to be reused / reassigned to array below
    const criteriaLevel1: Criteria = {
      fieldName: 'Criteria name',
      fieldValue: 'Crit-Level-1',
      childItems: []
    };
                       // parent object 2 - child of  object 1
                       // to be reused / reassigned to array below
                       // into - childItems[0]
    const criteriaLevel2: Criteria = {
      fieldName: 'Criteria name',
      fieldValue: 'Crit-Level-2',
      childItems: []
    };
                       // list of 3 different items to be assigned to array 
                       // into - childItems[0].childItems[0] of each array record.
    const itemsABC: string[] = [
      'item AAA',
      'item BBB',
      'item CCC'
    ];

    const criteriaArray = [];
    let ix = 0;

    itemsABC.forEach(item => {

      console.log('item: ' + item);

      criteriaArray[ix] = [];
      criteriaArray[ix][0] = criteriaLevel1;
      criteriaArray[ix][0].childItems[0] = criteriaLevel2;
      criteriaArray[ix][0].childItems[0].childItems[0] = {
          fieldName: 'name',
          fieldValue: item + '-' + ix
      };
      ix++;
    });

    // output test

    for (let i = 0; i < 3; i++) {
      console.log('ix: ' + i);
      for (const itemA of criteriaArray[i]) {
        console.log('a: ' + itemA.fieldName + ' - ' + itemA.fieldValue);
        for (const itemB of itemA.childItems) {
          console.log('b: ' + itemB.fieldName + ' - ' + itemB.fieldValue);
          for (const itemC of itemB.childItems) {
            console.log('c: ' + itemC.fieldName + ' - ' + itemC.fieldValue);
          }
        }
      }

    }
  }

I get this output:

index: 0 - inserting item: item AAA

index: 1 - inserting item: item BBB

index: 2 - inserting item: item CCC

ix: 0

a: Criteria name - Crit-Level-1

b: Criteria name - Crit-Level-2

c: name - item CCC-2 // BUT I am expecting here: item AAA-0

ix: 1

a: Criteria name - Crit-Level-1

b: Criteria name - Crit-Level-2

c: name - item CCC-2 // BUT I am expecting here: item BBB-1

ix: 2

a: Criteria name - Crit-Level-1

b: Criteria name - Crit-Level-2

c: name - item CCC-2 // YES as expected here: item CCC-2

What am I doing wrong ?

2 Answers 2

1

You're assigning references to the objects, so all three elements of criteriaArray are pointing to the same instances of criteriaLevel1 and criteriaLevel2.


You have a few options to maintain the same pattern:

Spread syntax (but note the compatibility matrix)

criteriaArray[ix][0] = {...criteriaLevel1, childItems: []};
criteriaArray[ix][0].childItems[0] = {...criteriaLevel2, childItems: []};

const criteriaLevel1 = {
  fieldName: 'Criteria name',
  fieldValue: 'Crit-Level-1',
  childItems: []
};

const criteriaLevel2 = {
  fieldName: 'Criteria name',
  fieldValue: 'Crit-Level-2',
  childItems: []
};
  
const itemsABC = [
  'item AAA',
  'item BBB',
  'item CCC'
];

const criteriaArray = [];
let ix = 0;

itemsABC.forEach(item => {
  console.log('item: ' + item);

  criteriaArray[ix] = [];
  criteriaArray[ix][0] = {...criteriaLevel1, childItems: []};
  criteriaArray[ix][0].childItems[0] = {...criteriaLevel2, childItems: []};
  criteriaArray[ix][0].childItems[0].childItems[0] = {
    fieldName: 'name',
    fieldValue: item + '-' + ix
  };
    ix++;
});

for (let i = 0; i < 3; i++) {
  console.log('ix: ' + i);
  for (const itemA of criteriaArray[i]) {
    console.log('a: ' + itemA.fieldName + ' - ' + itemA.fieldValue);
    for (const itemB of itemA.childItems) {
      console.log('b: ' + itemB.fieldName + ' - ' + itemB.fieldValue);
      for (const itemC of itemB.childItems) {
        console.log('c: ' + itemC.fieldName + ' - ' + itemC.fieldValue);
      }
    }
  }
}

Object.assign()

criteriaArray[ix][0] = Object.assign({}, criteriaLevel1, {childItems: []});
criteriaArray[ix][0].childItems[0] = Object.assign({}, criteriaLevel2, {childItems: []});

const criteriaLevel1 = {
  fieldName: 'Criteria name',
  fieldValue: 'Crit-Level-1',
  childItems: []
};

const criteriaLevel2 = {
  fieldName: 'Criteria name',
  fieldValue: 'Crit-Level-2',
  childItems: []
};
  
const itemsABC = [
  'item AAA',
  'item BBB',
  'item CCC'
];

const criteriaArray = [];
let ix = 0;

itemsABC.forEach(item => {
  console.log('item: ' + item);

  criteriaArray[ix] = [];
  criteriaArray[ix][0] = Object.assign({}, criteriaLevel1, {childItems: []});
  criteriaArray[ix][0].childItems[0] = Object.assign({}, criteriaLevel2, {childItems: []});
  criteriaArray[ix][0].childItems[0].childItems[0] = {
    fieldName: 'name',
    fieldValue: item + '-' + ix
  };
    ix++;
});

for (let i = 0; i < 3; i++) {
  console.log('ix: ' + i);
  for (const itemA of criteriaArray[i]) {
    console.log('a: ' + itemA.fieldName + ' - ' + itemA.fieldValue);
    for (const itemB of itemA.childItems) {
      console.log('b: ' + itemB.fieldName + ' - ' + itemB.fieldValue);
      for (const itemC of itemB.childItems) {
        console.log('c: ' + itemC.fieldName + ' - ' + itemC.fieldValue);
      }
    }
  }
}

Note that both methods perform a shallow copy, so you have to manually account for nested object references.


An alternative approach is creating a helper function to instantiate a new object.

function generateCriteria(fieldName, fieldValue) {
  return () => ({
    fieldName: fieldName,
    fieldValue: fieldValue,
    childItems: []
  });
}

const criteriaLevel1 = generateCriteria('Criteria name', 'Crit-Level-1');
const criteriaLevel2 = generateCriteria('Criteria name', 'Crit-Level-2');

const itemsABC = [
  'item AAA',
  'item BBB',
  'item CCC'
];

const criteriaArray = [];
let ix = 0;

itemsABC.forEach(item => {
  console.log('item: ' + item);

  criteriaArray[ix] = [];
  criteriaArray[ix][0] = criteriaLevel1();
  criteriaArray[ix][0].childItems[0] = criteriaLevel2();
  criteriaArray[ix][0].childItems[0].childItems[0] = {
    fieldName: 'name',
    fieldValue: item + '-' + ix
  };
    ix++;
});

for (let i = 0; i < 3; i++) {
  console.log('ix: ' + i);
  for (const itemA of criteriaArray[i]) {
    console.log('a: ' + itemA.fieldName + ' - ' + itemA.fieldValue);
    for (const itemB of itemA.childItems) {
      console.log('b: ' + itemB.fieldName + ' - ' + itemB.fieldValue);
      for (const itemC of itemB.childItems) {
        console.log('c: ' + itemC.fieldName + ' - ' + itemC.fieldValue);
      }
    }
  }
}

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

Comments

0

Object by value and Object by reference is a tricky thing. When you assign criterialLevel1 in 1st iteration , in the next iteration you are actually updating the reference of criteriaLeve1 variable all the time.

criteriaArray[ix] = [];
criteriaArray[ix][0] = {...criteriaLevel1, childItems: []}; // creating a new object and spreading it, Note the child Items created new else as it is an array would have been passed by reference as well.
criteriaArray[ix][0].childItems[0] = {...criteriaLevel2, childItems: []};
criteriaArray[ix][0].childItems[0].childItems[0] = {
  fieldName: 'name',
  fieldValue: item + '-' + ix
};

I would recommend that rather defining those variable why not assign them directly and you can do it in a single line:

criteriaArray[ix] = [{
  fieldName: 'Criteria name', 
  fieldValue: 'Crit-Level-1', 
  childItems: [{
    fieldName: 'Criteria name', 
    fieldValue: 'Crit-Level-2', 
    childItems: [{
      fieldName: 'name',
      fieldValue: item + '-' + ix
    }]
  }] 
}];

2 Comments

Thank you. This would be a solution, but since a quite longer list and more parent child levels may be involved, therefore another approach will be needed.
checkout the 1st one where the object is spreaded and a new one is created with it

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.