Reputation: 121
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
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:
Data
item to its array of attributes
DataAttributes
objectsDataAttributes
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
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
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
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