ebody: key:value in Objekt finden

problematische Seite

Hallo,

jedes key:value paar aus searchKeyValuePair[] soll in inThisObject{} gesucht werden und jeder Erfolg mit true gespeichert werden. Ich muss festhalten, wie oft es einen Treffer gab.

/** Prüfe jedes key:value paar... */
const searchKeyValuePair = [
  {Title: 'Batman'},
  {Genre: 'Science-Fiction'},
  {Title: 'Prometheus'},
  {Duration: 1},
  {Title: 'Prometheus'},
];

/** ...ob es in dem Array vorkommt  */
const inThisObject = {
  Title: 'Prometheus',
  Genre: 'Science-Fiction',
  Tags: 'Weltall',
  Duration: 2
};

Probiert habe ich schon sehr viel, siehe auskommentierten Code.. Das ist jetzt die aktuellste Version:

/**
Erzeugt ein Array aus searchKeyValuePair[] und aus jedem Objekt ein Array.
[
  ['Title', 'Batman'], 
  ['Genre', 'Science-Fiction'],
  ['Title', 'Prometheus'],
  ...
]
*/ 
const arrSearchKeyValuePairs = searchKeyValuePair.map(searchItem => Object.entries(searchItem));
console.log('arrSearchKeyValuePairs: ', arrSearchKeyValuePairs);

/**
Erzeugt ein Array aus inThisObject[] und aus jedem key:value ein Array.
[
  ['Title', 'Prometheus'],
  ['Genre', 'Science-Fiction'],
  ['Tags', 'Weltall'],
  ['Duration', 2],
]
*/
const arrInThisObject = Object.entries(inThisObject);
console.log('arrInThisObject: ', arrInThisObject);

console.log('result: ', arrSearchKeyValuePairs.filter(item => arrInThisObject.some(item)));

Aber ich bekomme es einfach nicht hin. Hat jemand einen Tipp?

Gruß ebody

  1. problematische Seite

    Hallo ebody,

    wieder was gelernt. Object.entries kannte ich nicht 😂

    Letztlich erzeugst Du auf diesem Weg aus searchKeyValuePair und inThisObject kompatible Strukturen, nämlich ein Array von Arrays mit zwei Einträgen, in denen jeweils Eigenschaftsname und Wert stehen.

    Bei arrSearchKeyValuePair bist Du aber über das Ergebnis im Irrtum, denn Object.entries erzeugt ein Array aus Arraypärchen, ein Pärchen pro Objekteigenschaft, und deshalb sieht arrSearchKeyValuePair so aus:

    [
      [['Title', 'Batman']], 
      [['Genre', 'Science-Fiction']],
      [['Title', 'Prometheus']],
      ...
    ]
    

    Du sieht das zusätzliche Klammerpaar? Das willst Du nicht. Du müsstest aus dem map Callback das Ergebnis Object.entries(searchItem)[0] zurückgeben. Dann käme das raus, was Du Dir gedacht hast.

    Aber dann kommst Du mit some, und some braucht eine Callback-Funktion. Du übergibst aber eins von diesen Arrays mit zwei Einträgen. D.h. das zentrale Problem, wie Du in einem Objekt eine Eigenschaft findest, deren Name Du erst zur Laufzeit kennst, hast Du nicht gelöst.

    Mein Fazit: Deine Transformierung mit Object.entries macht Dir die Aufgabe nicht leichter, eher schwerer.

    Für eine Lösung wäre aber auch noch die Frage interessant: wie sieht es mit case-sensitivity aus? Wenn Du im searchKeyValuePair den Eintrag { title: 'Batman' } hast, soll dieser Eintrag die Eigenschaft Title in inThisObject abfragen? Also kleines title vs großes Title? Nehmen wir erstmal an, dass Du dafür keinen Treffer willst.

    Ob Du dann .filter auf searchKeyValuePairs oder arrSearchKeyValuePairs anwendest, ist ziemlich egal. Ich würde hier noch die Frage stellen, ob searchKeyValuePairs tatsächlich so aussehen muss, wie es aussieht. Ein keyValuePair sollte so aussehen:
    { key: 'Title', value: 'Batman' },
    denn dann

    • ist sichergestellt, dass es auch ein PAAR ist
    • musst Du nicht den Namen des Schlüssels aus dem Objekt herausfummeln

    Wenn Du deine searchKeyValuePairs so aufbaust, kannst Du auf Object.entries ganz verzichten. Statt dessen rufst Du filter direkt auf searchKeyValuePairs auf, schaust mit der hasOwnProperty Methode nach, ob inThisObject eine Eigenschaft mit dem Namen hat, der in searchItem.key steht und vergleichst dann die Werte. Das ist ein Einzeiler.

    Statt hasOwnProperty könnte man auch den in Operator verwenden (Huch, der fehlt im Selfwiki?!), aber in liefert auch für geerbte Eigenschaften UND Methoden true - das kann nach hinten losgehen.

    Links:

    Rolf

    --
    sumpsi - posui - obstruxi
    1. problematische Seite

      Hallo an alle,

      vielen Dank für eure Infos und Lösungsansätze. Ich denke, ich habe es jetzt umgesetzt bekommen.

      /** Prüfe jedes key:value paar... */
      const searchKeyValuePair = [
        {Title: 'Batman'},
        {Genre: 'Science-Fiction'},
        {Title: 'Prometheus'},
        {Duration: 1},
        {Title: 'Prometheus'},
        {Genre: 'Batman'},
        {Title: ''},
      ];
      
      /** ...ob es in dem Array vorkommt  */
      const inThisObject = {
        Title: 'Prometheus',
        Genre: 'Science-Fiction',
        Tags: 'Weltall',
        Duration: 2
      };
      
      /** Filter alle das Array searchKeyValuePair, so dass nur die key:value Paare übrig bleiben, die in inThisObject vorkommen. */
      let data = searchKeyValuePair.filter(search => 
        /** Enthält inThisObject den Key von search (z.B. {Title: 'Batman'})? */
        inThisObject.hasOwnProperty(Object.keys(search).toString()) 
        /** Enthält der Wert von search (z.B. {Title: 'Batman'}) den gleichen Wert wie inThisObject['mit dem aktuellen Key']? */
        && Object.values(search).includes(inThisObject[Object.keys(search).toString()])
      );
      
      console.log('data: ', data); 
      

      Für eine Lösung wäre aber auch noch die Frage interessant: wie sieht es mit case-sensitivity aus?

      In dem Fall nicht notwendig, da die Keys auf jeden Fall identisch sind.

      Ich würde hier noch die Frage stellen, ob searchKeyValuePairs tatsächlich so aussehen muss, wie es aussieht.

      Wäre so auch möglich { key: 'Title', value: 'Batman' }. Gut zu wissen, dass man Objekte auch so schreiben kann.

      Was mir darüber hinaus noch auffällt ist, dass dein Array searchKeyValuePairs doppelte Einträge enthält. Entspricht das deinen Daten oder oder sind die key-value Paare in Wirklichkeit eindeutig?

      Entspricht den Daten und soll so sein.

      Gruß ebody

      1. problematische Seite

        Hallo ebody,

        Wäre so auch möglich { key: 'Title', value: 'Batman' }. Gut zu wissen, dass man Objekte auch so schreiben kann.

        Das hast Du gewusst. Das ist nichts anders als { Title: 'Batman'}, nur mit zwei Eigenschaften statt einer. Der Vorteil ist aber, dass Du bei meinem Vorschlag die Eigenschaftsnamen kennst und nicht erst heraussuchen musst. Deine Schreibweise kann dann interessant werden, wenn deine SearchItems nach mehr als einer Eigenschaft gleichzeitig suchen sollen.

        ....filter(search => 
          /** Enthält inThisObject den Key von search (z.B. {Title: 'Batman'})? */
          inThisObject.hasOwnProperty(Object.keys(search).toString()) 
          /** Enthält der Wert von search (z.B. {Title: 'Batman'}) den gleichen Wert wie inThisObject['mit dem aktuellen Key']? */
          && Object.values(search).includes(inThisObject[Object.keys(search).toString()]))
        

        Weia. Mit search.key statt Object.keys(search).toString() wäre das deutlich lesbarer. Aber wenn Du unbedingt bei deiner Form der Searchitems bleiben willst, dann mach es so:

        ....filter(search => {
          /** Zu findendes Property ist einziger Key in search (z.B. {Title: 'Batman'}) */
          const key = Object.keys(search)[0];
          /** Enthält inThisObject den Key ? */
          /** und stimmt search[key] mit dem Wert von inThisObject[key] überein? */
          return inThisObject.hasOwnProperty(key) && search[key] == inThisObject[key])
               
        })
        

        Man muss jetzt geschweifte Klammern um den Body der Pfeilfunktion setzen und das return-Statement hinschreiben, weil eine lokale Variable deklariert wird. Das kann man vermeiden, indem man die lokale Variable als Parameter tarnt:

        ....filter((search, key) => 
          /** Zu findendes Property ist einziger Key in search (z.B. {Title: 'Batman'}) */
          inThisObject.hasOwnProperty(key = Object.keys(search)[0]) 
          /** Stimmt search[key] mit dem Wert von inThisObject[key] überein? */
          && inThisObject[key] == search[key])
        )
        

        aber das ist ein Beispiel für üblen und kaum verständlichen Code und nicht zu empfehlen. Ein Minifizierer darf sowas vielleicht produzieren, aber lesbarer Code ist das nicht.

        Was spricht dagegen?

        • Der filter-Callback hat laut Spec 3 Parameter: Wert, Index und Array. Die muss man nicht alle übernehmen, aber wenn man einen davon deklariert und ihn dann für einen ganz anderen Zweck verwendet, ist das unfallträchtig.
        • Der Missbrauch von key ist darüber hinaus noch als Seiteneffekt in der Parameterübergabe an hasOwnProperty versteckt.

        Rolf

        --
        sumpsi - posui - obstruxi
  2. problematische Seite

    Ich vermute aus dem Inhalt, Du willst eigentlich:

    const haystack = [
    	[
    		{Title: 'Batman'},
    		{Genre: 'Science-Fiction'}
    	], [
    		{Title: 'Prometheus'},
    		{Duration: 1}
    	], [
    		{Title: 'Prometheus'}
    	]
    ];
    
    console.log ( haystack);
    

    Den Heuhaufen kannst Du mit

    haystack.forEach ( 
    	// item => console.log( item )
    	item => item.forEach( 
    		pair => showKeyValue( pair ) 
    	)
    )
    
    
    function showKeyValue( o ) {
    	//console.log( o );
    	const arr = Object.keys( o );
    	//console.log( arr );
    	arr.forEach( 
    		key => console.log( key + ' = ' + o[key] )
    	)
    }
    

    ausgeben. Dafür bekomme ich sicher gleich „Dresche“, weil das Jahr 2010 nach irgendetwas ruft… Aber ich hab schon mal das passende Ergebnis:

    [
      [ { Title: 'Batman' }, { Genre: 'Science-Fiction' } ],
      [ { Title: 'Prometheus' }, { Duration: 1 } ],
      [ { Title: 'Prometheus' } ]
    ]
    Title = Batman
    Genre = Science-Fiction
    Title = Prometheus
    Duration = 1
    Title = Prometheus
    

    Naja. Und wenn Du schon dabei bist, sollte es kein langer Weg sein, um

    • statt der Ausgabe den Vergleich durchzuführen
    • Deinen Array / Dein Objekt zu füttern.
  3. problematische Seite

    Hallo ebody,

    dass deine Objekt-Representation der key-value Paare nicht optimal ist, hat Rolf ja schon geschrieben, ebenso zur Problematik mit der case sensitivity.

    Was mir darüber hinaus noch auffällt ist, dass dein Array searchKeyValuePairs doppelte Einträge enthält. Entspricht das deinen Daten oder oder sind die key-value Paare in Wirklichkeit eindeutig?

    Falls du wie in deinem Beispiel doppelte Einträge haben solltest und dann, wie von Rolf vorgeschlagen, filter auf dem Array searchKeyValuePairs aufrufen und checken würdest, ob der jeweilige Eintrag in dem Objekt inThisObject vorhanden ist, würdest du Einträge gegebenenfalls mehrfach zählen, was du vermutlich nicht möchtest.

    Für dein konkretes Beispiel würdest du 3 Treffer bekommen, da das Paar (Title, Prometheus) doppelt gezählt wird.

    Für diesen Ansatz müsstest du also zunächst sicherstellen, dass die key-value Paare höchstens einmal vorkommen.

    Oder du testest anders herum, das heißt, du filterst die Einträge in inThisObject indem du prüfst, ob der Eintrag in searchKeyValuePairs enthalten ist.

    Viele Grüße,

    Matthias