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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.