Question

Angular: pull sub array values into main object with dynamic subarray

I'd like to pull sub array values into my main object. The subarray size is dynamic.

Starting with this:

myArray = [{'ID' : 1,
        'addDetail' : ['test 1', 'test 2']}]

I expect this array:

myArray = [{'ID' : 1, 'line1' : 'test 1', 'line2' : 'test 2'
        'addDetail' : ['test 1', 'test 2']}]

I tried the below code:

this.partiesdatalist = this.partiesdatalist.map(item => {
  const addDetail = item.addDetail;
  addDetail?.forEach((detail, index) => {
    item[`line${index + 1}`] = detail;
  });
  return item;
});

but it produces this error:

Element implicitly has an 'any' type because expression of type 'line${number}' can't be used to index type

On this line:

item[`line${index + 1}`] = detail;
 2  44  2
1 Jan 1970

Solution

 2

You have to first defining typing for all the properties. We can use [key: string]: any to allow for dynamic properties on the interface.

export interface PartiesDetail {
  [key: string]: any;
  ID: number;
  addDetail: Array<string>;
}

Full Code:

import { Component } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import 'zone.js';

export interface PartiesDetail {
  [key: string]: any;
  ID: number;
  addDetail: Array<string>;
}

@Component({
  selector: 'app-root',
  standalone: true,
  template: ``,
})
export class App {
  partiesdatalist: Array<PartiesDetail> = [
    { ID: 1, addDetail: ['test 1', 'test 2'] },
  ];
  ngOnInit() {
    this.partiesdatalist = this.partiesdatalist.map((item: PartiesDetail) => {
      const addDetail = item.addDetail;
      addDetail?.forEach((detail: string, index) => {
        item[`line${index + 1}`] = detail;
      });
      return item;
    });
  }
}

bootstrapApplication(App);

Stackblitz Demo

2024-07-21
Naren Murali

Solution

 1

While it's advisable to do things Naren's way for any project you expect to scale, for quick proof of concepts and other micro-scale tasks, you can also do this without needing to define the interface:

import {
  Component,
  provideExperimentalZonelessChangeDetection,
} from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import { CommonModule } from '@angular/common';

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [CommonModule],
  template: `{{partiesdatalist | json}}`,
})
export class App {
  partiesdatalist: Array<any> = [{ ID: 1, addDetail: ['test 1', 'test 2'] }];
  ngOnInit() {
    this.partiesdatalist = this.partiesdatalist.map((item: any) => {
      const addDetail = item.addDetail;
      addDetail?.forEach((detail: string, index: any) => {
        item[`line${index + 1}`] = detail;
      });
      return item;
    });
    this.partiesdatalist.push(
      this.partiesdatalist[0]['addDetail' as keyof Array<any>].pop()
    );
  }
}

bootstrapApplication(App, {
  providers: [provideExperimentalZonelessChangeDetection()],
});

Just be careful, as there have been many 'quick and dirty prototype's that ended up getting built into a full system...

I have also added an additional line to achieve your desired ordering, though this will only work for your specific use-case and should not be applied generically.

Stackblitz Demo

2024-07-21
Some of the Things