rkras
rkras

Reputation: 121

How to filter data in a nested array in Angular

I have an Angular Service which sends a data service call and returns an array of objects containing multiple layers of data. I want to be able to filter thrhough that data and return only the object that contains a specific value.

        this.service.getRecords().pipe(
        map(item => item.filter(items => items.attributes['identifier'].value === '101723102')),
        tap(item => console.log(item)),

    ).subscribe();

The attributes[] has thosands of objects like the one stated below:

[20000]: {identifier:"1239213", something: "42142", something: "42142", something: "42142".....}

I am trying to filter and return the objects if the identifier is equal to something but am not sure how to access the value since it is nested and I cannot use dot notation. Current code returns error: Cannot read property 'identifier' of undefined

[EDIT]: This is the service I am using

export class DataViewerService {

_recordDetails: Observable<DataClass[]>;

// resource service contains code to retrieve data constructor(private _resourceService: ResourceService, ) { }

    getRecords() {
    return this._recordDetails = this._resourceService.search(CONSTANT, {})
        .pipe(map(extractRecords))
        .pipe(shareReplay()) as Observable<Data[]>;

}


function extractRecords(result) {
const records = [];
if (!isNullOrUndefined(result)) {
    result.forEach(r => {
        if (!isNullOrUndefined(r['payload'])) {
            records.push(r.payload);
        }
    });
}
return records;

}

[EDIT]: Items Object is as follows:

{
  attributes: [],
  descrtiption: "",
  name: "",
  label: ""
}

I need to access the objects within the attributes[] of filter which looks like
attributes: [
{
     identifier: "1243212",
     something: "",
...
}

This is the interface and the object returns is of type Data

export interface Data {
name: string;
label: string;
attributes: DataAttributes[];

}

interface DataAttributes {
    attributes: [
           identifier: string;
           something: string;
     ];
    something: string;
...
 
}

And the service returns the attributes[] which contains the values I want to filter

Upvotes: 0

Views: 3465

Answers (4)

Jeff Mercado
Jeff Mercado

Reputation: 134801

It's very unclear what exactly you're trying to do. Assuming your goal is to take your Data objects and select certain DataAttributes objects that matches some name, then you could do something like this:

  1. map each Data item to its array of attributes
  2. flatten those arrays of attributes to a single stream of DataAttributes objects
  3. filter those DataAttributes objects by the desired name
const records: Observable<Data[]> = ...;
const selectAttribute = 'some name';
records.pipe(
  concatMap(records => records.map(({attributes}) => attributes)),
  concatAll(),
  filter(({name}) => name === selectAttribute)
);

https://stackblitz.com/edit/so-62805815

Upvotes: 0

MarksASP
MarksASP

Reputation: 506

If you don't mind not having an algorithm for this you can rely on lodash for these kind of operations with objects in Javascript (not Angular per se).

First install lodash (npm install lodash).

Since you want to filter by a nested key, you should use filter method with a function as predicate.

import * as _ from 'lodash';

// let's asume a variable called attributes holds your array of objects
const filteredData = _.filter(attributes, function(item) { return item.value === 'something'; })

I don't know if I understand the structure of your data. You can read de docs for filter here.

Upvotes: 0

Supun De Silva
Supun De Silva

Reputation: 1487

Using New model interface as follows

export interface DataModel {
  name: string;
  label: string;
  attributes: object[];
}

Using a Mock Data as follows

{
    attributes: [
        { identifier: '1239213', something: 100 },
        { identifier: 'foo', someOtherThing: 100},
        { identifier: '101723102', description: "101723102"},
        { notIdentifier: '101723102', description: "101723102"}        
    ],
    name: "",
    label: ""
}

Using a Mock Data Service as follows

import { Injectable } from '@angular/core';
import { Subject, BehaviorSubject } from 'rxjs';
import { DataModel } from './models/data';

@Injectable()
export class DataService {

  private mockData : DataModel = {
    attributes: [
        { identifier: '1239213', something: 100 },
        { identifier: 'foo', someOtherThing: 100},
        { identifier: '101723102', description: "101723102"},
        { notIdentifier: '101723102', description: "101723102"}    
    ],
    name: "",
    label: ""
  }

  private sbj : BehaviorSubject<{}> = new BehaviorSubject<any>(this.mockData);
  constructor() { }

  getRecords() {
    return this.sbj;
  }
}

Do the following

import { Component, VERSION, OnInit } from '@angular/core';
import { DataService } from './data.service';
import { map, tap } from 'rxjs/operators';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent implements OnInit {
  name = 'Angular ' + VERSION.major;

  public filteredObjStr = ''
  constructor(private service: DataService) {

  }

  ngOnInit() {
    this.service.getRecords().pipe(
        map(item =>   {
          if (item['attributes'] == null) {
            return []
          }
          return item['attributes'].filter(x => x['identifier'] === '101723102')
        }),
        tap(filteredAttribs => 
        {
          this.filteredObjStr = JSON.stringify(filteredAttribs, null, 2);
          console.log('filteredAttribs : ', filteredAttribs);
        })

    ).subscribe();
  }

  public filter_changed(value) {
    console.log(value)
  }
}

Example - Check App console

https://stackblitz.com/edit/angular-ivy-uvxxjk

Upvotes: 0

fro
fro

Reputation: 442

It might work

map(item => item.filter(items => items.attributes['identifier'] && items.attributes['identifier'].value === '101723102')),

or

map(item => item.filter(items => item.attributes !== undefined && items.attributes['identifier'] != undefined && items.attributes['identifier'].value === '101723102'))

also it will be better if you edit DataAttributes like this:

interface DataAttributes {
    name: string;
    label: string;
    identifier?: any;
}

Upvotes: 1

Related Questions