Typescript - foreach/map Array of Objects

  • Jetzt anmelden. Es dauert nur 2 Minuten und ist kostenlos!
18 Juni 2014
409
13
18
31
#1
Hallo Leute,

ich kämpfe hier jetzt schon seit ein paar Tagen (nicht am Stück) mit einem Problem, finde aber irgendwie keinen weg, der mich auch nur einen Schritt vorwärts bringt. Ich baue gerade eine Ionic-App mit Angular 5.

Ich hab ein Object, das wiederum ein Array of objects enthält. Die dazugehörige JSON-Datei sieht so aus:
Code:
{
  "data": [
    {
      "_objectInstance": {
        "id": 383,
        "name": {
          "givenName": "",
          "honorificSuffix": "",
          "formatted": "Schmidt",
          "middleName": "",
          "familyName": "Schmidt",
          "honorificPrefix": ""
        },
        "nickname": "",
        "phoneNumbers": [
          {
            "value": "+493658553212",
            "pref": false,
            "id": 0
          }
        ],
        "organizations": [
          {
            "pref": "false",
            "title": "",
            "name": "IHK",
            "department": ""
          }
        ],
        "note": ""
      }
    },..........
Den Inhalt hole ich mir über das Angular-Modul HttpClient:
Code:
  loadConJSON() {
    this.http.get<Contacts[]>('../../assets/data/contacts.json')
    .subscribe(contacts => {
      console.log(contacts);
    });
  };
Und da muss jetzt irgendwie ein foreach oder besser: .map rein. Denn ich möchte mir ein neues Array nach folgender Struktur bauen:
name = contacts.name.familyName
forename = contacts.name.givenName usw.

Das Problem hierbei ist ja, dass ich bspw. nicht mit "keys" arbeiten kann, denn es gibt ja nur einen Key und zwar "data".

Habt ihr eine Idee oder einen Lösungsvorschlag, wie das gehen könnte?
Danke im Voraus für alle hilfreichen Antworten!
 

basti1012

Aktives Mitglied
26 November 2017
682
60
28
37
Minden
chat.sebastian1012.bplaced.net
#2
Verstehe nicht ganz was du meinst.Du bekommst ja data zurück wie du sagst und da steht doch alles drinne was du brauchst oder nicht. Also willst du aus data mehre arrays machen wie name, vorname, givenname, middlename usw.
Oder liege ich falsch?
 
Zuletzt bearbeitet:
Zustimmungen: Bourbon
18 Juni 2014
409
13
18
31
#3
Ja prinzipiell erhalte ich alles, was ich brauche. Nur möchte ich das jetzt 1. etwas verdichten und 2. mir ein neues Array mit ID und Email basteln, an den Server schicken und prüfen, ob die email in der DB vorhanden ist.

Ergo: altes Array bestehen lassen und ein neues zweites bauen.

EDIT: zum Thema Email irgendwohin schicken - das wird später gegen einen Hash-Wert geändert, jetzt soll es erstmal einfach sein und funktionieren :)
 

Tronjer

Moderator
Team
Moderator
8 Oktober 2010
5.020
411
83
Berlin
#5
Und da muss jetzt irgendwie ein foreach oder besser: .map rein. Denn ich möchte mir ein neues Array nach folgender Struktur bauen
Bin mir nicht sicher, ob ich das richtig verstehe. Du hast als Ausgangsobjekt kein Array, sondern ein Objekt. Insofern gibt es da auch nichts zu iterieren.

Code:
const myObj = contacts['data'][0]['_objectInstance']['name']; // Objekt
Im zweiten Schritt würde eine Iteration über myObj nur dann Sinn ergeben, falls dessen Properties unbekannt sind. Ansonsten lassen diese sich direkt zuweisen.

Und dann bleibt die Frage, ob du im Ergebnis tatsächlich ein Array haben willst, oder nicht lieber ein reduziertes Objekt.
 
Zustimmungen: Bourbon

scbawik

Senior HTML'ler
14 Juli 2011
2.463
429
83
#6
Hallo Leute,

ich kämpfe hier jetzt schon seit ein paar Tagen (nicht am Stück) mit einem Problem, finde aber irgendwie keinen weg, der mich auch nur einen Schritt vorwärts bringt. Ich baue gerade eine Ionic-App mit Angular 5.

Ich hab ein Object, das wiederum ein Array of objects enthält. Die dazugehörige JSON-Datei sieht so aus:
Code:
{
  "data": [
    {
      "_objectInstance": {
        "id": 383,
        "name": {
          "givenName": "",
          "honorificSuffix": "",
          "formatted": "Schmidt",
          "middleName": "",
          "familyName": "Schmidt",
          "honorificPrefix": ""
        },
        "nickname": "",
        "phoneNumbers": [
          {
            "value": "+493658553212",
            "pref": false,
            "id": 0
          }
        ],
        "organizations": [
          {
            "pref": "false",
            "title": "",
            "name": "IHK",
            "department": ""
          }
        ],
        "note": ""
      }
    },..........
Den Inhalt hole ich mir über das Angular-Modul HttpClient:
Code:
  loadConJSON() {
    this.http.get<Contacts[]>('../../assets/data/contacts.json')
    .subscribe(contacts => {
      console.log(contacts);
    });
  };
Und da muss jetzt irgendwie ein foreach oder besser: .map rein. Denn ich möchte mir ein neues Array nach folgender Struktur bauen:
name = contacts.name.familyName
forename = contacts.name.givenName usw.

Das Problem hierbei ist ja, dass ich bspw. nicht mit "keys" arbeiten kann, denn es gibt ja nur einen Key und zwar "data".

Habt ihr eine Idee oder einen Lösungsvorschlag, wie das gehen könnte?
Danke im Voraus für alle hilfreichen Antworten!
Habe einen 14 Stunden Tag hinter mir und bin daher nicht mehr ganz auf der Höhe... Aber so sollte es gehen:

JavaScript:
this.httpClient.get(...)
    .map((contacts) => {
        return contacts.data.map((contact) => {
            return {
                name: contact._objectInstance.name,
                email: contact._objectInstance.email
            }
        });
    ))
    .subscribe(contacts => {
        console.log(contacts);
    });
 
Zustimmungen: Bourbon
18 Juni 2014
409
13
18
31
#7
@Tronjer
Ein reduziertes Objekt würde da wahrscheinlich genauso reichen, wobei ich das wohl erst merken werden, wenn ich die Daten dann serverseitig weiterverarbeite.

@scbawik
Danke für den Code-Schnipsel. Sag mal wolltest du nicht eher kürzer treten als 14h am Tag zu arbeiten? ;)
Also ich hab dein Code mal eingefügt und um die entsprechenden Punkte ergänzt (einzelne Zeichen und den JSON Pfad, this.http.get). Lediglich bei folgenden Punkt wird noch gemeckert:
Code:
return contacts.data.map((contact) => {
Da erhalte ich folgenden Fehler:
Die Eigenschaft "data" ist für den Typ "Object" nicht vorhanden.
Woran könnte das liegen?

EDIT:
Mit .map "bearbeite" ich aber nur das alte Array, richtig? Ein gänzlich neues wird dadurch ja sicherlich nicht erzeugt.
 
Zuletzt bearbeitet:

Tronjer

Moderator
Team
Moderator
8 Oktober 2010
5.020
411
83
Berlin
#8
Ich habe es mal nachgebaut. Hier werden alle Properties von name in das neue Objekt gepushed.
Code:
import {Component, OnInit} from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})

export class AppComponent implements OnInit {

  constructor(private http: HttpClient) {}

  ngOnInit(): void {
    this.http.get('data/contacts.json')
      .subscribe(
        result => {
          const contact = result['data'][0]['_objectInstance']['name'];
          const reduced = {};

          Object.entries(contact).forEach(
            ([key, value]) => {
              reduced[key] = key;
              reduced[value] = value;
            }
          );

          console.log('reduced', reduced);
        }
      );
  }

}
btw. Die Variable reduced zeigt im Webstorm einen Error, wenn man sie mit let deklariert. Deshalb habe ich hier const verwendet.

edit: Falls es wirklich mal um komplexe Objektmanipulationen gehen sollte, bietet sich natürlich lodash an.
https://lodash.com/
 
Zustimmungen: Bourbon
18 Juni 2014
409
13
18
31
#10
Es erscheint ein Fehler bei
Code:
Object.entries(contact).forEach(
Die Eigenschaft "entries" ist für den Typ "ObjectConstructor" nicht vorhanden.

Sonst wird erstmal nirgends geschimpft.

Hab das hier gefunden:
https://stackoverflow.com/questions...-not-exist-on-type-objectconstructor/45422950

Wahrscheinlich müsste ich auch ES2015 auf ES2017 umstellen, aber wo?
Oder hat das nichts mit dem Fehler hier zu tun?
 
Zuletzt bearbeitet:

Tronjer

Moderator
Team
Moderator
8 Oktober 2010
5.020
411
83
Berlin
#11
Jo, Object.entries() ist ES2017. Hängt wohl an der Angular-CLI Version. Die aktuelle (1.7+) sollte das zu ES5 kompilieren.

Aber das geht natürlich auch mit for ... in ...
Code:
import {Component, OnInit} from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})

export class AppComponent implements OnInit {

  constructor(private http: HttpClient) {}

  ngOnInit(): void {
    this.http.get('data/contacts.json')
      .subscribe(
        result => {
          const contact = result['data'][0]['_objectInstance']['name'];
          let reduced = {};

          for (const prop in contact) {
            if( contact.hasOwnProperty(prop) ) {
              reduced[prop] = prop;
              reduced[contact[prop]] = contact[prop];
            }
          }

          console.log('reduced', reduced);
        }
      );
  }

}
 
Zustimmungen: Bourbon
18 Juni 2014
409
13
18
31
#12
Also prinzipiell funktioniert es. Es zeigt derzeit aufgrund "result['data'][0]" ja nur den ersten Eintrag an, aber den reduziert es.
Hier mal ein Bild mit der Ausgabe:

Wieso steht da "Schmidt: "Schmidt" "? Sollte da nicht eher "familyName: Schmidt" stehen oder so was?

Hab auch nochmal eine Fragen bezüglich des Code-Schnipsels von @scbawik :
Code:
this.httpClient.get(...)
    .map((contacts) => {
        return contacts.data.map((contact) => {
            return {
                name: contact._objectInstance.name,
                email: contact._objectInstance.email
            }
        });
    ))
    .subscribe(contacts => {
        console.log(contacts);
    });
Da schimpft es bei folgenden Punkt:
Code:
return contacts.data.map((contact) => {
Hier kommt folgender Fehler:
Die Eigenschaft "data" ist für den Typ "Object" nicht vorhanden.
Wie lässt sich das denn beheben?
 

Tronjer

Moderator
Team
Moderator
8 Oktober 2010
5.020
411
83
Berlin
#13
@Bourbon

Hast recht, da hatte sich noch ein Fehler eingeschlichen. Aber so wie ich dein letztes Posting verstehe, willst du ja kein reduziertes Objekt, sondern ein Array von reduzierten Objekten.

Also nochmal:
contacts.json mit zwei Objekten in data
Code:
{
  "data": [
    {
      "_objectInstance": {
        "id": 383,
        "name": {
          "givenName": "",
          "honorificSuffix": "",
          "formatted": "Schmidt",
          "middleName": "",
          "familyName": "Schmidt",
          "honorificPrefix": ""
        },
        "nickname": "",
        "phoneNumbers": [
          {
            "value": "+493658553212",
            "pref": false,
            "id": 0
          }
        ],
        "organizations": [
          {
            "pref": "false",
            "title": "",
            "name": "IHK",
            "department": ""
          }
        ],
        "note": ""
      }
    },
    {
      "_objectInstance": {
        "id": 393,
        "name": {
          "givenName": "",
          "honorificSuffix": "",
          "formatted": "Foo",
          "middleName": "",
          "familyName": "Foo",
          "honorificPrefix": ""
        },
        "nickname": "",
        "phoneNumbers": [
          {
            "value": "+493658553212",
            "pref": false,
            "id": 0
          }
        ],
        "organizations": [
          {
            "pref": "false",
            "title": "",
            "name": "IHK",
            "department": ""
          }
        ],
        "note": ""
      }
    }
  ]
}
Und die Klasse:
Code:
import {Component, OnInit} from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {

  constructor(private http: HttpClient) {}

  ngOnInit(): void {
    this.http.get('data/contacts.json')
      .subscribe(
        result => {
          const sourceArr = result['data'];
          let targetArr = [];

          console.log('sourceArr', sourceArr);

          for (const index in sourceArr) {
            if(sourceArr.hasOwnProperty(index)) {
              let src = sourceArr[index]['_objectInstance']['name'];
              let obj = {};
              for (const prop in src) {
                if(src.hasOwnProperty(prop)) {
                  obj[prop] = src[prop]
                }
              }
              targetArr.push(obj);
            }
          }
          console.log('targetArr', targetArr);
        }
      );
  }
}
Wobei man anstatt des inneren for ... in ... die Properties auch direkt zuweisen könnte, sofern diese bekannt sind.
 
Zustimmungen: Bourbon
18 Juni 2014
409
13
18
31
#14
So, zurück vom Bett (wie es sich für einen guten Arbeitnehmer gehört: pünktlich Freitag Abend Grippe).

EDIT//
Es geht! Super, tausend Dank an dieser Stelle! ;)

Kann ich dir noch eine relativ simple Frage stellen:
Wie weise ich denn im inneren FOR genau explizit einzelne Variablen zu?
Also nach diesem Muster bspw:
Code:
targetArr.object.name.givenName = sourceArr.name.givenName
targetArr.object.emails[] = sourceArr.emails[]
 
Zuletzt bearbeitet:

Tronjer

Moderator
Team
Moderator
8 Oktober 2010
5.020
411
83
Berlin
#15
Du könntest Zeile 27 anpassen und auf den key emails prüfen, wobei der in meinem Beispiel allerdings nicht existiert.

Sofern dir der Umgang mit verschachtelten Objekten noch Schwierigkeiten bereitet, würde ich das an deiner Stelle üben. ;)
 
Zustimmungen: Bourbon
18 Juni 2014
409
13
18
31
#16
Mehr üben ist ja das Ziel ;) und dadurch, dass du mir ein Grundgerüst erstellt hast, kann ich auch basteln :)

Also Id und die Namen funktionieren schon mal, nur mit dem inneren FOR für die Emails kämpfe ich noch. Ich halte leeren E-Mail-Einträgen nur ein leeres Object (so muss das ja sein), sobald aber eine Email-Adresse bzw. ein Value in Emails vorhanden ist, kommt nur "undefined".

Was mache ich jetzt wieder falsch?
Code:
  loadConJSON_Own() {
    this.http.get('../../assets/data/contacts.json')
      .subscribe(
        result => {
          const sourceArr = result['data'];
          let targetArr = [];
 
          console.log('sourceArr', sourceArr);
 
          for (const index in sourceArr) {
            if(sourceArr.hasOwnProperty(index)) {
              let src = sourceArr[index]['_objectInstance']['emails'];
              let obj = {
                id:{},
                givenName:{},
                familyName:{},
                emails:{
                  values:{}
                }
              };
              obj.id = sourceArr[index]['_objectInstance'].id;
              obj.givenName = sourceArr[index]['_objectInstance']['name'].givenName;
              obj.familyName = sourceArr[index]['_objectInstance']['name'].familyName;
              for (const prop in src) {
                if(src.hasOwnProperty(prop)) {
                  obj.emails.values = src.value
                }
                targetArr.push(obj.emails.values);
              }
              targetArr.push(obj);
            }
          }
          console.log('targetArr', targetArr);
        }
      );
  }
 

Tronjer

Moderator
Team
Moderator
8 Oktober 2010
5.020
411
83
Berlin
#17
- Du initialisierst id, givenName, familyName in let obj als leere Objektliterale.
- Eine Email besitzt immer mehrere Properties. Insofern sollte emails ein Array sein.

Einfache Fassung ohne Angular:
Code:
  const testObj = {
    data: [
      {
        '_objectInstance': {
          'id': 1337,
          'name': {
            'givenName': 'John',
            'familyName': 'Doe',
            'emails': [{'sender': 'john_doe', 'text': 'foobar'}],
            'ignore_me': false
          }
        }
      },
      {
        '_objectInstance': {
          'id': 1338,
          'name': {
            'givenName': 'Jane',
            'familyName': 'Austen',
            'emails': [{'sender': 'john_doe', 'text': 'blabla'}],
            'ignore_me': false
          }
        }
      }
    ]
  };

  const sourceObj = testObj['data'];
  const validKeys = ['givenName', 'familyName', 'emails'];
  let targetArr = [];


  for (const key in sourceObj) {
    if(sourceObj.hasOwnProperty(key)) {
      const src = sourceObj[key]['_objectInstance']['name'];
      let obj = {
        id: sourceObj[key]['_objectInstance']['id']
      };
      targetArr.push(obj);
      for (const prop in src) {
        if (src.hasOwnProperty(prop) && validKeys.indexOf(prop) > -1) {
          obj[prop] = src[prop]
        }
      }
    }
  }

  console.log('targetArr', targetArr);
 
Zustimmungen: Bourbon
18 Juni 2014
409
13
18
31
#18
Also id, givenName und familyName hatte es richtig ausgegeben, habe es aber dennoch geändert, weil das ja so einfach logischer ist. Wozu erst ein leeres Object erstellen und dann füllen, wenn alles in einem Ritt geht.

Was macht denn folgende Zeile?
Code:
validKeys.indexOf(prop) > -1
 

Tronjer

Moderator
Team
Moderator
8 Oktober 2010
5.020
411
83
Berlin
#19
Wozu erst ein leeres Object erstellen und dann füllen, wenn alles in einem Ritt geht.
Wieso befüllen, wenn die Werte eh überschrieben werden? Außerdem sind die leeren Objektliterale falsch. Du hast hinterhe nämlich ein Number, zwei Strings und ein Array in der Variable. Bei einem Interface in TypeScript würde der Fehler sichtbar.

Prüft, ob die Keys im Array stehen. Das erspart eine verknüpfte Abfrage mit if.
 
18 Juni 2014
409
13
18
31
#20
Als Zusammenfassung hier nochmal mein endgültiger, funktionsfähiger Code:

Code:
  loadConJSON() {
    this.http.get('../../assets/data/contacts.json')
      .subscribe(
        result => {
          const sourceArr = result['data'];
          console.log('sourceArr',sourceArr);
          for (let entry of sourceArr) {
              let srcEmails = entry['_objectInstance']['emails'];
              let conEmails = []
              if (srcEmails) {
                const ifEmails = srcEmails.map(x => x.value);
                conEmails = ifEmails;
              }
              let srcPhone = entry['_objectInstance']['phoneNumbers'];
              let conPhone = []
              if (srcPhone) {
                const ifPhone = srcPhone.map(x => x.value);
                conPhone = ifPhone;
              }
              let obj = {
                id: entry['_objectInstance'].id,
                givenName: entry['_objectInstance']['name'].givenName,
                familyName: entry['_objectInstance']['name'].familyName,
                emails: conEmails,
                phone: conPhone
              };
              this.targetArr.push(obj);
            }
          console.log('targetArr', this.targetArr);
        }
      );
  }