WebHU - Programozási kérdések és válaszok

Hogyan lehet mélyen összehasonlítani két objektumot a lodash segítségével?

Van 2 beágyazott objektum, amelyek különböznek egymástól, és tudnom kell, hogy van-e különbség az egyik beágyazott tulajdonságukban.

var a = {};
var b = {};

a.prop1 = 2;
a.prop2 = { prop3: 2 };

b.prop1 = 2;
b.prop2 = { prop3: 3 };

Az objektum lehetne sokkal összetettebb, több beágyazott tulajdonsággal. De ez egy jó példa. Lehetőségem van rekurzív függvényeket használni, vagy valami lodash-t...

28.07.2015

  • A részletes összehasonlításhoz stackoverflow.com/a/46003894/696535 01.09.2017
  • _.isEqual(value, other) Mély összehasonlítást végez két érték között, hogy megállapítsa, egyenértékűek-e. lodash.com/docs#isEqual 04.09.2018
  • JSON.stringify() 09.10.2019
  • A JSON.stringify() hibás: JSON.stringify({a:1,b:2}) !== JSON.stringify({b:2,a:1}) 10.12.2019

Válaszok:


1

Egy egyszerű és elegáns megoldás a _.isEqual használata, amely részletes összehasonlítást végez:

var a = {};
var b = {};

a.prop1 = 2;
a.prop2 = { prop3: 2 };

b.prop1 = 2;
b.prop2 = { prop3: 3 };

console.log(_.isEqual(a, b)); // returns false if different
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>

Ez a megoldás azonban nem mutatja meg, hogy melyik tulajdonság különbözik.

28.07.2015
  • Tudom, hogy a válasz elég régi, de szeretném hozzátenni, hogy a _.isEqual meglehetősen bonyolult lehet. Ha átmásolod az objektumot, és megváltoztatsz benne néhány értéket, akkor is igaz lesz, mert a hivatkozás ugyanaz. Tehát óvatosnak kell lennie ennek a funkciónak a használatával. 13.09.2018
  • @oruckdeschel ha a hivatkozás ugyanaz, akkor ugyanaz az objektum. tehát egyenlő. ez egy trükkös mutató, nem pedig lodash. lodash fantasztikus. 25.09.2018

  • 2

    Ha tudnia kell, hogy mely tulajdonságok különböznek egymástól, használja a reduce() parancsot:

    _.reduce(a, function(result, value, key) {
        return _.isEqual(value, b[key]) ?
            result : result.concat(key);
    }, []);
    // → [ "prop2" ]
    
    28.07.2015
  • Vegye figyelembe, hogy ez csak az első szintű különböző tulajdonságokat adja ki. (Tehát ez nem igazán mély abban az értelemben, hogy a különböző tulajdonságokat adja ki.) 05.05.2016
  • Ezenkívül ez nem veszi fel a b-ben lévő tulajdonságokat, amelyek nincsenek az a-ban. 24.08.2016
  • és _.reduce(a, (result, value, key) => _.isEqual(value, b[key]) ? result : result.concat(key), []) egysoros ES6 megoldáshoz 04.11.2017
  • A kulcs:érték let edited = _.reduce(a, function(result, value, key) { return _.isEqual(value, b[key]) ? result : result.concat( { [key]: value } ); }, []); kulcsot összefűző verzió 23.09.2018

  • 3

    Aki belebotlik ebbe a témába, itt van egy teljesebb megoldás. Összehasonlít két objektumot, és megadja az összes olyan tulajdonság kulcsát, amelyek vagy csak az Object1-ben, csak az Object2-ben vagy a objektum1-ben és objektum2-ben is, de eltérő értékük:

    /*
     * Compare two objects by reducing an array of keys in obj1, having the
     * keys in obj2 as the intial value of the result. Key points:
     *
     * - All keys of obj2 are initially in the result.
     *
     * - If the loop finds a key (from obj1, remember) not in obj2, it adds
     *   it to the result.
     *
     * - If the loop finds a key that are both in obj1 and obj2, it compares
     *   the value. If it's the same value, the key is removed from the result.
     */
    function getObjectDiff(obj1, obj2) {
        const diff = Object.keys(obj1).reduce((result, key) => {
            if (!obj2.hasOwnProperty(key)) {
                result.push(key);
            } else if (_.isEqual(obj1[key], obj2[key])) {
                const resultKeyIndex = result.indexOf(key);
                result.splice(resultKeyIndex, 1);
            }
            return result;
        }, Object.keys(obj2));
    
        return diff;
    }
    

    Íme egy példa kimenet:

    // Test
    let obj1 = {
        a: 1,
        b: 2,
        c: { foo: 1, bar: 2},
        d: { baz: 1, bat: 2 }
    }
    
    let obj2 = {
        b: 2, 
        c: { foo: 1, bar: 'monkey'}, 
        d: { baz: 1, bat: 2 }
        e: 1
    }
    getObjectDiff(obj1, obj2)
    // ["c", "e", "a"]
    

    Ha nem érdekelnek a beágyazott objektumok, és ki akarja hagyni a lodash-t, akkor a _.isEqual karakterrel helyettesítheti a normál érték összehasonlítását, pl. obj1[key] === obj2[key].

    15.11.2016
  • Ez a választott válasz csak az egyenlőség tesztelésére helyes. Ha tudnia kell, hogy mik a különbségek, nincs egyértelmű módja annak, hogy felsoroljuk őket, de ez a válasz nagyon jó, csak megadja a legfelső szintű tulajdonságkulcsok listáját, ahol különbség van. (És függvényként adja meg a választ, ami használhatóvá teszi.) 12.12.2016
  • Mi a különbség az _.isEqual(obj1, obj2) használata között? Mit tesz a hasOwnProperty csekk hozzáadásával, amit az _.isEqual nem? Abból a feltételezésből indultam ki, hogy ha az obj1-nek van egy tulajdonsága, amivel az obj2-nek nincs, akkor az _.isEqual nem ad vissza igazat.. ? 04.08.2017
  • @Jaked222 - a különbség az, hogy az isEqual egy logikai értéket ad vissza, amely megmondja, hogy az objektumok egyenlőek-e vagy sem, míg a fenti függvény megmondja, miben a különbség a két objektum között (ha különböznek). Ha csak az érdekli, hogy két objektum azonos-e, akkor az isEqual is elég. Sok esetben azonban tudni szeretné, mi a különbség két objektum között. Példa erre, ha valami előtt és után szeretné észlelni a változásokat, majd a változások alapján elküldeni egy eseményt. 05.08.2017

  • 4

    Adam Boduch válasza alapján írtam ezt a függvényt, amely két objektumot a lehető legmélyebb értelemben hasonlít össze strong>, olyan útvonalakat ad vissza, amelyeknek eltérő értéke van, valamint az egyik vagy másik objektumból hiányzó útvonalakat.

    A kódot nem a hatékonyságot szem előtt tartva írtuk, és az ezzel kapcsolatos fejlesztések nagyon örvendetesek, de itt van az alapforma:

    var compare = function (a, b) {
    
      var result = {
        different: [],
        missing_from_first: [],
        missing_from_second: []
      };
    
      _.reduce(a, function (result, value, key) {
        if (b.hasOwnProperty(key)) {
          if (_.isEqual(value, b[key])) {
            return result;
          } else {
            if (typeof (a[key]) != typeof ({}) || typeof (b[key]) != typeof ({})) {
              //dead end.
              result.different.push(key);
              return result;
            } else {
              var deeper = compare(a[key], b[key]);
              result.different = result.different.concat(_.map(deeper.different, (sub_path) => {
                return key + "." + sub_path;
              }));
    
              result.missing_from_second = result.missing_from_second.concat(_.map(deeper.missing_from_second, (sub_path) => {
                return key + "." + sub_path;
              }));
    
              result.missing_from_first = result.missing_from_first.concat(_.map(deeper.missing_from_first, (sub_path) => {
                return key + "." + sub_path;
              }));
              return result;
            }
          }
        } else {
          result.missing_from_second.push(key);
          return result;
        }
      }, result);
    
      _.reduce(b, function (result, value, key) {
        if (a.hasOwnProperty(key)) {
          return result;
        } else {
          result.missing_from_first.push(key);
          return result;
        }
      }, result);
    
      return result;
    }
    

    Kipróbálhatja a kódot ezzel a részlettel (ajánlott a teljes oldalas módban futtatni):

    var compare = function (a, b) {
    
      var result = {
        different: [],
        missing_from_first: [],
        missing_from_second: []
      };
    
      _.reduce(a, function (result, value, key) {
        if (b.hasOwnProperty(key)) {
          if (_.isEqual(value, b[key])) {
            return result;
          } else {
            if (typeof (a[key]) != typeof ({}) || typeof (b[key]) != typeof ({})) {
              //dead end.
              result.different.push(key);
              return result;
            } else {
              var deeper = compare(a[key], b[key]);
              result.different = result.different.concat(_.map(deeper.different, (sub_path) => {
                return key + "." + sub_path;
              }));
    
              result.missing_from_second = result.missing_from_second.concat(_.map(deeper.missing_from_second, (sub_path) => {
                return key + "." + sub_path;
              }));
    
              result.missing_from_first = result.missing_from_first.concat(_.map(deeper.missing_from_first, (sub_path) => {
                return key + "." + sub_path;
              }));
              return result;
            }
          }
        } else {
          result.missing_from_second.push(key);
          return result;
        }
      }, result);
    
      _.reduce(b, function (result, value, key) {
        if (a.hasOwnProperty(key)) {
          return result;
        } else {
          result.missing_from_first.push(key);
          return result;
        }
      }, result);
    
      return result;
    }
    
    var a_editor = new JSONEditor($('#a')[0], {
      name: 'a',
      mode: 'code'
    });
    var b_editor = new JSONEditor($('#b')[0], {
      name: 'b',
      mode: 'code'
    });
    
    var a = {
      same: 1,
      different: 2,
      missing_from_b: 3,
      missing_nested_from_b: {
        x: 1,
        y: 2
      },
      nested: {
        same: 1,
        different: 2,
        missing_from_b: 3
      }
    }
    
    var b = {
      same: 1,
      different: 99,
      missing_from_a: 3,
      missing_nested_from_a: {
        x: 1,
        y: 2
      },
      nested: {
        same: 1,
        different: 99,
        missing_from_a: 3
      }
    }
    
    a_editor.set(a);
    b_editor.set(b);
    
    var result_editor = new JSONEditor($('#result')[0], {
      name: 'result',
      mode: 'view'
    });
    
    var do_compare = function() {
      var a = a_editor.get();
      var b = b_editor.get();
      result_editor.set(compare(a, b));
    }
    #objects {} #objects section {
      margin-bottom: 10px;
    }
    #objects section h1 {
      background: #444;
      color: white;
      font-family: monospace;
      display: inline-block;
      margin: 0;
      padding: 5px;
    }
    .jsoneditor-outer, .ace_editor {
    min-height: 230px !important;
    }
    button:hover {
      background: orangered;
    }
    button {
      cursor: pointer;
      background: red;
      color: white;
      text-align: left;
      font-weight: bold;
      border: 5px solid crimson;
      outline: 0;
      padding: 10px;
      margin: 10px 0px;
    }
    <link href="https://cdnjs.cloudflare.com/ajax/libs/jsoneditor/5.5.10/jsoneditor.min.css" rel="stylesheet" />
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jsoneditor/5.5.10/jsoneditor.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <div id="objects">
      <section>
        <h1>a (first object)</h1>
        <div id="a"></div>
      </section>
      <section>
        <h1>b (second object)</h1>
        <div id="b"></div>
      </section>
      <button onClick="do_compare()">compare</button>
      <section>
        <h1>result</h1>
        <div id="result"></div>
      </section>
    </div>

    02.01.2017
  • Kijavítottam a hibát, de hogy tudd, ellenőrizze a kulcs meglétét egy objektumon belül b a b.hasOwnProperty(key) vagy key in b használatával, nem b[key] != undefined-el. A b[key] != undefined-t használó régi verziónál a függvény hibás eltérést adott vissza a undefined-t tartalmazó objektumokhoz, mint például a compare({disabled: undefined}, {disabled: undefined})-ben. Valójában a régi verziónak is voltak problémái a null-al; Az ehhez hasonló problémákat elkerülheti, ha a == és != helyett mindig a === és !==-t használja. 24.03.2017

  • 5

    Íme egy tömör megoldás a Lodash használatával:

    _.differenceWith(a, b, _.isEqual);
    

    Vegye figyelembe, hogy mindkét bemenetnek tömbnek kell lennie (esetleg egy objektum tömbjének).

    13.04.2016
  • Úgy tűnik, számomra nem működik tárgyakkal. Ehelyett egy üres tömböt ad vissza. 22.11.2017
  • Szintén üres tömböt kapunk a Lodash 4.17.4-el 25.01.2018
  • @Z.Khullah Ha ez így működött, az nincs dokumentálva. 29.01.2018
  • @Brendon , @THughes, @aristidesfl bocs, kevertem a dolgokat, működik objektumok tömbjeivel, de nem mély objektum-összehasonlításhoz. Mint kiderült, ha egyik paraméter sem tömb, a lodash csak a [] értéket adja vissza. 29.01.2018
  • úgy tűnik, csak tömbökkel működik. De nem probléma egy objektumot tömbbe helyezni. _.differenceWith([object1], [object2], _.isEqual); ha a visszaadott tömb üres - ez azt jelenti, hogy - nincs különbség, ha a tömb nem üres - különbség van 14.09.2020

  • 6

    Ha rekurzív módon meg szeretné mutatni, hogy egy objektum miben különbözik a többitől, használhatja a _.reduce függvényt az _.isEqual és az _.isPlainObject elemekkel kombinálva. Ebben az esetben összehasonlíthatja, hogy a miben különbözik b-től, vagy miben különbözik b a-tól:

    const objectA = {
      a: {
        1: "SAME WILL BE MISSING IN RESULT", 
        2: "BBB", 
        3: [1, 2, 3]
      }, 
      b: "not", 
      c: "foo bar"
    };
    const objectB = {
      a: {
        1: "SAME WILL BE MISSING IN RESULT",
        2: [1, 2]
      }, 
      b: "foo", 
      c: "bar"
    };
    
    const diff = function(obj1, obj2) {
      return _.reduce(obj1, function(result, value, key) {
    if (_.isPlainObject(value)) {
      result[key] = diff(value, obj2[key]);
    } else if (!_.isEqual(value, obj2[key])) {
      result[key] = value;
    }
    return result;
      }, {});
    };
    
    const diffAOverB = diff(objectA, objectB);
    const diffBOverA = diff(objectA, objectB);
    console.log(diffAOverB);
    console.log(diffBOverA);
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/lodash.min.js"></script>

    10.01.2018

    7

    Ez a kód egy objektumot ad vissza minden olyan tulajdonsággal, amelynek eltérő értéke van, és mindkét objektum értékét is. Hasznos a különbség naplózásához.

    var allkeys = _.union(_.keys(obj1), _.keys(obj2));
    var difference = _.reduce(allkeys, function (result, key) {
      if ( !_.isEqual(obj1[key], obj2[key]) ) {
        result[key] = {obj1: obj1[key], obj2: obj2[key]}
      }
      return result;
    }, {});
    
    02.10.2016

    8

    Egyszerűen használható _.isEqual módszer, minden összehasonlításnál működni fog...

    • Megjegyzés: Ez a módszer támogatja a tömbök, tömbpufferek, logikai értékek, * dátumobjektumok, hibaobjektumok, térképek, számok, Object objektumok, regexek, * halmazok, karakterláncok, szimbólumok és beírt tömbök összehasonlítását. Object objektumok összehasonlítása * saját, nem öröklött, felsorolható tulajdonságaik alapján történik. A funkciók és a DOM * csomópontok nem támogatottak.

    Tehát ha van lent:

     const firstName = {name: "Alireza"};
     const otherName = {name: "Alireza"};
    

    Ha így tesz: _.isEqual(firstName, otherName);,

    az igaz értéket adja vissza

    És ha const fullName = {firstName: "Alireza", familyName: "Dezfoolian"};

    Ha így tesz: _.isEqual(firstName, fullName);,

    false értéket ad vissza

    28.10.2019

    9

    Megszúrtam egy Adam Boduch kódot, hogy mély különbséget adjak ki – ez teljesen nem tesztelt, de a darabok ott vannak:

    function diff (obj1, obj2, path) {
        obj1 = obj1 || {};
        obj2 = obj2 || {};
    
        return _.reduce(obj1, function(result, value, key) {
            var p = path ? path + '.' + key : key;
            if (_.isObject(value)) {
                var d = diff(value, obj2[key], p);
                return d.length ? result.concat(d) : result;
            }
            return _.isEqual(value, obj2[key]) ? result : result.concat(p);
        }, []);
    }
    
    diff({ foo: 'lol', bar: { baz: true }}, {}) // returns ["foo", "bar.baz"]
    
    06.03.2017
  • Úgy működik, mint egy bűbáj, csak az obj1 és obj2 sorrendje fontos. Például: diff({}, { foo: 'lol', bar: { baz: true }}) // returns [] 31.03.2017

  • 10

    Mély összehasonlítás a (beágyazott) tulajdonságok sablonjával az ellenőrzéshez

    function objetcsDeepEqualByTemplate(objectA, objectB, comparisonTemplate) {
      if (!objectA || !objectB) return false
    
      let areDifferent = false
      Object.keys(comparisonTemplate).some((key) => {
        if (typeof comparisonTemplate[key] === 'object') {
          areDifferent = !objetcsDeepEqualByTemplate(objectA[key], objectB[key], comparisonTemplate[key])
          return areDifferent
        } else if (comparisonTemplate[key] === true) {
          areDifferent = objectA[key] !== objectB[key]
          return areDifferent
        } else {
          return false
        }
      })
    
      return !areDifferent
    }
    
    const objA = { 
      a: 1,
      b: {
        a: 21,
        b: 22,
      },
      c: 3,
    }
    
    const objB = { 
      a: 1,
      b: {
        a: 21,
        b: 25,
      },
      c: true,
    }
    
    // template tells which props to compare
    const comparisonTemplateA = {
      a: true,
      b: {
        a: true
      }
    }
    objetcsDeepEqualByTemplate(objA, objB, comparisonTemplateA)
    // returns true
    
    const comparisonTemplateB = {
      a: true,
      c: true
    }
    // returns false
    objetcsDeepEqualByTemplate(objA, objB, comparisonTemplateB)
    

    Ez a konzolban működni fog. A tömb támogatása szükség esetén hozzáadható

    01.09.2017

    11

    Ahogy kérdeztük, itt van egy rekurzív objektum-összehasonlító függvény. És még egy kicsit. Feltételezve, hogy az ilyen funkció elsődleges használata az objektumvizsgálat, van mondanivalóm. A teljes mélységű összehasonlítás rossz ötlet, ha egyes különbségek nem relevánsak. Például a vak mély összehasonlítás a TDD állításokban szükségtelenné teszi a teszteket. Emiatt egy sokkal értékesebb részleges különbséget szeretnék bemutatni. Ez a szál egy korábbi hozzájárulásának rekurzív analógja. Figyelmen kívül hagyja az a-ban nem található kulcsokat

    var bdiff = (a, b) =>
        _.reduce(a, (res, val, key) =>
            res.concat((_.isPlainObject(val) || _.isArray(val)) && b
                ? bdiff(val, b[key]).map(x => key + '.' + x) 
                : (!b || val != b[key] ? [key] : [])),
            []);
    

    A BDiff lehetővé teszi a várt értékek ellenőrzését, miközben más tulajdonságokat is tolerál, és pontosan ezt szeretné az automatikus ellenőrzéshez. Ez lehetővé teszi mindenféle fejlett állítás létrehozását. Például:

    var diff = bdiff(expected, actual);
    // all expected properties match
    console.assert(diff.length == 0, "Objects differ", diff, expected, actual);
    // controlled inequality
    console.assert(diff.length < 3, "Too many differences", diff, expected, actual);
    

    Visszatérve a teljes megoldáshoz. Teljes hagyományos diff felépítése bdiff-fel triviális:

    function diff(a, b) {
        var u = bdiff(a, b), v = bdiff(b, a);
        return u.filter(x=>!v.includes(x)).map(x=>' < ' + x)
        .concat(u.filter(x=>v.includes(x)).map(x=>' | ' + x))
        .concat(v.filter(x=>!u.includes(x)).map(x=>' > ' + x));
    };
    

    A fenti függvény futtatása két összetett objektumon valami ehhez hasonló kimenetet eredményez:

     [
      " < components.0.components.1.components.1.isNew",
      " < components.0.cryptoKey",
      " | components.0.components.2.components.2.components.2.FFT.min",
      " | components.0.components.2.components.2.components.2.FFT.max",
      " > components.0.components.1.components.1.merkleTree",
      " > components.0.components.2.components.2.components.2.merkleTree",
      " > components.0.components.3.FFTResult"
     ]
    

    Végül, annak érdekében, hogy bepillantást nyerhessünk az értékek különbségébe, érdemes közvetlenül eval() a diff kimenetet. Ehhez szükségünk van a bdiff csúnyább verziójára, amely szintaktikailag helyes útvonalakat ad ki:

    // provides syntactically correct output
    var bdiff = (a, b) =>
        _.reduce(a, (res, val, key) =>
            res.concat((_.isPlainObject(val) || _.isArray(val)) && b
                ? bdiff(val, b[key]).map(x => 
                    key + (key.trim ? '':']') + (x.search(/^\d/)? '.':'[') + x)
                : (!b || val != b[key] ? [key + (key.trim ? '':']')] : [])),
            []);
    
    // now we can eval output of the diff fuction that we left unchanged
    diff(a, b).filter(x=>x[1] == '|').map(x=>[x].concat([a, b].map(y=>((z) =>eval('z.' + x.substr(3))).call(this, y)))));
    

    Ez valami ehhez hasonlót fog kiadni:

    [" | components[0].components[2].components[2].components[2].FFT.min", 0, 3]
    [" | components[0].components[2].components[2].components[2].FFT.max", 100, 50]
    

    MIT licenc ;)

    10.05.2018

    12

    Tudnom kell, hogy van-e különbség valamelyik beágyazott tulajdonságukban

    Más válaszok potenciálisan kielégítő megoldásokat kínálnak erre a problémára, de ez elég nehéz és gyakori, hogy úgy tűnik, van egy nagyon népszerű csomag a probléma megoldására. mély-objektum-diff.

    A csomag használatához npm i deep-object-diff, majd:

    const { diff } = require('deep-object-diff');
    var a = {};
    var b = {};
    
    a.prop1 = 2;
    a.prop2 = { prop3: 2 };
    
    b.prop1 = 2;
    b.prop2 = { prop3: 3 };
    
    if (!_.isEqual(a, b)) {
      const abDiff = diff(a, b);
      console.log(abDiff);
      /*
      {
        prop2: {
          prop3: 3
        }
      }
      */
    }
    
    // or alternatively
    const abDiff = diff(a, b);
    if(!_.isEmpty(abDiff)) {
      // if a diff exists then they aren't deeply equal
      // perform needed actions with diff...
    }
    

    Íme egy részletesebb eset a tulajdonok közvetlenül a dokumentumokból való törlésével kapcsolatban:

    const lhs = {
      foo: {
        bar: {
          a: ['a', 'b'],
          b: 2,
          c: ['x', 'y'],
          e: 100 // deleted
        }
      },
      buzz: 'world'
    };
    
    const rhs = {
      foo: {
        bar: {
          a: ['a'], // index 1 ('b')  deleted
          b: 2, // unchanged
          c: ['x', 'y', 'z'], // 'z' added
          d: 'Hello, world!' // added
        }
      },
      buzz: 'fizz' // updated
    };
    
    console.log(diff(lhs, rhs)); // =>
    /*
    {
      foo: {
        bar: {
          a: {
            '1': undefined
          },
          c: {
            '2': 'z'
          },
          d: 'Hello, world!',
          e: undefined
        }
      },
      buzz: 'fizz'
    }
    */
    

    A megvalósítás részleteiért és egyéb használati információkért tekintse meg az adott repót.

    04.08.2020
  • szép könyvtár a mély különbségekhez.. 04.12.2020

  • 13

    A lodash/aláhúzás használata nélkül írtam ezt a kódot, és jól működik számomra az objektum1 és az objektum2 mély összehasonlításában.

    function getObjectDiff(a, b) {
        var diffObj = {};
        if (Array.isArray(a)) {
            a.forEach(function(elem, index) {
                if (!Array.isArray(diffObj)) {
                    diffObj = [];
                }
                diffObj[index] = getObjectDiff(elem, (b || [])[index]);
            });
        } else if (a != null && typeof a == 'object') {
            Object.keys(a).forEach(function(key) {
                if (Array.isArray(a[key])) {
                    var arr = getObjectDiff(a[key], b[key]);
                    if (!Array.isArray(arr)) {
                        arr = [];
                    }
                    arr.forEach(function(elem, index) {
                        if (!Array.isArray(diffObj[key])) {
                            diffObj[key] = [];
                        }
                        diffObj[key][index] = elem;
                    });
                } else if (typeof a[key] == 'object') {
                    diffObj[key] = getObjectDiff(a[key], b[key]);
                } else if (a[key] != (b || {})[key]) {
                    diffObj[key] = a[key];
                } else if (a[key] == (b || {})[key]) {
                    delete a[key];
                }
            });
        }
        Object.keys(diffObj).forEach(function(key) {
            if (typeof diffObj[key] == 'object' && JSON.stringify(diffObj[key]) == '{}') {
                delete diffObj[key];
            }
        });
        return diffObj;
    }
    
    29.06.2017

    14

    Tudom, hogy ez nem válaszol közvetlenül az OP kérdésére, de a lodash eltávolításának módja vezetett ide. Szóval remélhetőleg ez segít valakinek, aki hasonló helyzetben van, mint én.

    A jóváírás a következő címre érkezett: @JohanPersson. Ebből a válaszból építettem ki a mélyen beágyazott értékek összehasonlítását és a különbségekre vonatkozó kulcshivatkozást

    getObjectDiff = (obj1, obj2) => {
        const obj1Props = Object.keys(obj1);
        const obj2Props = Object.keys(obj2);
    
        const keysWithDiffValue = obj1Props.reduce((keysWithDiffValueAccumulator, key) => {
          const propExistsOnObj2 = obj2.hasOwnProperty(key);
          const hasNestedValue = obj1[key] instanceof Object && obj2[key] instanceof Object;
          const keyValuePairBetweenBothObjectsIsEqual = obj1[key] === obj2[key];
    
          if (!propExistsOnObj2) {
            keysWithDiffValueAccumulator.push(key);
          } else if (hasNestedValue) {
            const keyIndex = keysWithDiffValueAccumulator.indexOf(key);
            if (keyIndex >= 0) {
              keysWithDiffValueAccumulator.splice(keyIndex, 1);
            }
            const nestedDiffs = getObjectDiff(obj1[key], obj2[key]);
            for (let diff of nestedDiffs) {
              keysWithDiffValueAccumulator.push(`${key}.${diff}`);
            }
          } else if (keyValuePairBetweenBothObjectsIsEqual) {
            const equalValueKeyIndex = keysWithDiffValueAccumulator.indexOf(key);
            keysWithDiffValueAccumulator.splice(equalValueKeyIndex, 1);
          }
          return keysWithDiffValueAccumulator;
        }, obj2Props);
    
        return keysWithDiffValue;
      }
    const obj1 = {a0: {a1: {a2: {a3: 'Im here'}}}};
    const obj2 = {a0: {a1: {a2: {a3: 'Not here', b3: 'some'}}}};
    console.log('final', getObjectDiff(obj1, obj2));

    06.07.2021

    15

    Adam Boduch válaszát kiegészítve ez a tulajdonságok különbségeit veszi figyelembe

    const differenceOfKeys = (...objects) =>
      _.difference(...objects.map(obj => Object.keys(obj)));
    const differenceObj = (a, b) => 
      _.reduce(a, (result, value, key) => (
        _.isEqual(value, b[key]) ? result : [...result, key]
      ), differenceOfKeys(b, a));
    
    01.10.2016

    16

    Ha csak a kulcsok összehasonlítására van szüksége:

     _.reduce(a, function(result, value, key) {
         return b[key] === undefined ? key : []
      }, []);
    
    27.10.2016

    17

    Itt van egy egyszerű Typescript Lodash mély különbség-ellenőrzővel, amely egy új objektumot hoz létre, amely csak a régi és egy új objektum közötti különbségeket tartalmazza.

    Például, ha rendelkezünk:

    const oldData = {a: 1, b: 2};
    const newData = {a: 1, b: 3};
    

    a kapott objektum a következő lenne:

    const result: {b: 3};
    

    Kompatibilis a többszintű mély objektumokkal is, tömböknél szükség lehet némi módosításra.

    import * as _ from "lodash";
    
    export const objectDeepDiff = (data: object | any, oldData: object | any) => {
      const record: any = {};
      Object.keys(data).forEach((key: string) => {
        // Checks that isn't an object and isn't equal
        if (!(typeof data[key] === "object" && _.isEqual(data[key], oldData[key]))) {
          record[key] = data[key];
        }
        // If is an object, and the object isn't equal
        if ((typeof data[key] === "object" && !_.isEqual(data[key], oldData[key]))) {
          record[key] = objectDeepDiff(data[key], oldData[key]);
        }
      });
      return record;
    };
    
    27.03.2019
  • A kérdés nem Typescript címkével van ellátva, de így is szép válasz 31.05.2021

  • 18

    Ez a követelmény volt a két json-frissítés közötti különbség eléréséhez az adatbázis-frissítések nyomon követéséhez. Talán valaki más is hasznosnak találja ezt.

    https://gist.github.com/jp6rt/7fcb6907e159d78e

    const {
      isObject,
      isEqual,
      transform,
      has,
      merge,
    } = require('lodash');
    const assert = require('assert');
    
    /**
     * Perform a symmetric comparison on JSON object.
     * @param {*} baseObj - The base object to be used for comparison against the withObj.
     * @param {*} withObj - The withObject parameter is used as the comparison on the base object.
     * @param {*} invert  - Because this is a symmetric comparison. Some values in the with object
     *                      that doesn't exist on the base will be lost in translation.
     *                      You can execute again the function again with the parameters interchanged.
     *                      However you will lose the reference if the value is from the base or with
     *                      object if you intended to do an assymetric comparison.
     *                      Setting this to true will do make sure the reference is not lost.
     * @returns           - The returned object will label the result of the comparison with the
     *                      value from base and with object.
     */
    const diffSym = (baseObj, withObj, invert = false) => transform(baseObj, (result, value, key) => {
      if (isEqual(value, withObj[key])
        && has(withObj, key)) {
        return;
      }
    
      if (isObject(value)
        && isObject(withObj[key])
        && !Array.isArray(value)) {
        result[key] = diffSym(value, withObj[key], invert);
        return;
      }
    
      if (!invert) {
        result[key] = {
          base: value,
          with: withObj[key],
        };
        return;
      }
    
      if (invert) {
        result[key] = {
          base: withObj[key],
          with: value,
        };
      }
    });
    
    /**
     * Perform a assymmetric comparison on JSON object.
     * @param {*} baseObj - The base object to be used for comparison against the withObj.
     * @param {*} withObj - The withObject parameter is used as the comparison on the base object.
     * @returns           - The returned object will label the values with
     *                      reference to the base and with object.
     */
    const diffJSON = (baseObj, withObj) => {
      // Deep clone the objects so we don't update the reference objects.
      const baseObjClone = JSON.parse(JSON.stringify(baseObj));
      const withObjClone = JSON.parse(JSON.stringify(withObj));
    
      const beforeDelta = diffSym(baseObjClone, withObjClone);
      const afterDelta = diffSym(withObjClone, baseObjClone, true);
    
      return merge(afterDelta, beforeDelta);
    };
    
    // By Example:
    
    const beforeDataObj = {
      a: 1,
      c: { d: 2, f: 3 },
      g: 4,
      h: 5,
    };
    const afterDataObj = {
      a: 2,
      b: 3,
      c: { d: 1, e: 1 },
      h: 5,
    };
    
    const delta = diffJSON(beforeDataObj, afterDataObj);
    
    // Assert expected result.
    assert(isEqual(delta, {
      a: { base: 1, with: 2 },
      b: { base: undefined, with: 3 },
      c: {
        d: { base: 2, with: 1 },
        e: { base: undefined, with: 1 },
        f: { base: 3, with: undefined },
      },
      g: { base: 4, with: undefined },
    }));
    
    20.08.2020

    19

    Ez az én megoldásom a problémára

    const _ = require('lodash');
    
    var objects = [{ 'x': 1, 'y': 2, 'z':3, a:{b:1, c:2, d:{n:0}}, p:[1, 2, 3]  }, { 'x': 2, 'y': 1, z:3, a:{b:2, c:2,d:{n:1}}, p:[1,3], m:3  }];
    
    const diffFn=(a,b, path='')=>_.reduce(a, function(result, value, key) {
    
        if(_.isObjectLike(value)){
          if(_.isEqual(value, b[key])){
            return result;
          }else{
    
    return result.concat(diffFn(value, b[key], path?(`${path}.${key}`):key))
          }
        }else{
    return _.isEqual(value, b[key]) ?
            result : result.concat(path?(`${path}.${key}`):key);
        }
        
    }, []);
    
    const diffKeys1=diffFn(objects[0], objects[1])
    const diffKeys2=diffFn(objects[1], objects[0])
    const diffKeys=_.union(diffKeys1, diffKeys2)
    const res={};
    
    _.forEach(diffKeys, (key)=>_.assign(res, {[key]:{ old: _.get(objects[0], key), new:_.get(objects[1], key)} }))
    
    res
    /*
    Returns
    {
      x: { old: 1, new: 2 },
      y: { old: 2, new: 1 },
      'a.b': { old: 1, new: 2 },
      'a.d.n': { old: 0, new: 1 },
      'p.1': { old: 2, new: 3 },
      'p.2': { old: 3, new: undefined },
      m: { old: undefined, new: 3 }
    }
    */
    
    03.11.2020


    21

    ennek alapja a @JLavoie, a lodash használatával

    let differences = function (newObj, oldObj) {
          return _.reduce(newObj, function (result, value, key) {
            if (!_.isEqual(value, oldObj[key])) {
              if (_.isArray(value)) {
                result[key] = []
                _.forEach(value, function (innerObjFrom1, index) {
                  if (_.isNil(oldObj[key][index])) {
                    result[key].push(innerObjFrom1)
                  } else {
                    let changes = differences(innerObjFrom1, oldObj[key][index])
                    if (!_.isEmpty(changes)) {
                      result[key].push(changes)
                    }
                  }
                })
              } else if (_.isObject(value)) {
                result[key] = differences(value, oldObj[key])
              } else {
                result[key] = value
              }
            }
            return result
          }, {})
        }
    

    https://jsfiddle.net/EmilianoBarboza/0g0sn3b9/8/

    20.04.2018

    22

    A Sridhar Gudimela válaszára való építkezés érdekében itt TypeScriptet használó módon frissítjük:

    ///  U T I L S
    
    interface LooseObjectInterface {
      [key: string]: any;
    };
    
    type inputOptions = LooseObjectInterface | any[];
    
    
    
    ///  E X P O R T
    
    export const objectCompare = (objectA: inputOptions, objectB: inputOptions): LooseObjectInterface => {
      let diffObj: LooseObjectInterface = {};
    
      switch(true) {
        case (Array.isArray(objectA)):
          objectA.forEach((elem: any, index: number) => {
            if (!Array.isArray(diffObj))
              diffObj = [];
    
            diffObj[index] = objectCompare(elem, (objectB || [])[index]);
          });
    
          break;
    
        case (objectA !== null && typeof objectA === "object"):
          Object.keys(objectA).forEach((key: any) => {
            if (Array.isArray(objectA[key])) {
              let arr = objectCompare(objectA[key], objectB[key]);
    
              if (!Array.isArray(arr))
                arr = [];
    
              arr.forEach((elem: any, index: number) => {
                if (!Array.isArray(diffObj[key]))
                  diffObj[key] = [];
    
                diffObj[key][index] = elem;
              });
            } else if (typeof objectA[key] === "object")
              diffObj[key] = objectCompare(objectA[key], objectB[key]);
            else if (objectA[key] !== (objectB || {})[key])
              diffObj[key] = objectA[key];
            else if (objectA[key] === (objectB || {})[key])
              delete objectA[key];
          });
    
          break;
    
        default:
          break;
      }
    
      Object.keys(diffObj).forEach((key: any) => {
        if (typeof diffObj[key] === "object" && JSON.stringify(diffObj[key]) === "{}")
          delete diffObj[key];
      });
    
      return diffObj;
    };
    

    EDIT: Az eredeti válaszom a Flow-t használta, ezért a negatív szavazatok (feltételezem, vagy talán azért, mert a válaszom nem a Lodash-t használta... de ha egy hasonló problémára válaszolok, az nem árt).

    31.10.2019

    23

    csak vanilla js-t használunk

    let a = {};
    let b = {};
    
    a.prop1 = 2;
    a.prop2 = { prop3: 2 };
    
    b.prop1 = 2;
    b.prop2 = { prop3: 3 };
    
    JSON.stringify(a) === JSON.stringify(b);
    // false
    b.prop2 = { prop3: 2};
    
    JSON.stringify(a) === JSON.stringify(b);
    // true

    adja meg itt a kép leírását

    09.10.2019
  • Ez a módszer nem mondja meg, hogy mely attribútumok különböznek egymástól. 10.10.2019
  • Ebben az esetben az attribútumok sorrendje befolyásolja az eredményt. 21.11.2019

  • 24
  • Ez érvénytelen. Nem lehet közvetlenül összehasonlítani az objektumokat ===-vel, a { a: 20 } === { a: 20 } hamis értéket ad vissza, mert összehasonlítja a prototípust. Az objektumok elsődleges összehasonlításának helyesebb módja az, ha a JSON.stringify()-ba csomagoljuk őket 13.08.2017
  • if (f === s) true; - csak rekurzióra szolgál. Igen a: 20 } === { a: 20 } hamis értéket ad vissza, és a következő feltételre lép 30.08.2017
  • miért nem csak _.isEqual(f, s)? :) 30.08.2017
  • Ez egy végtelen rekurziós hurkot eredményez, mert ha a f egy objektum, és eléri a if (_.isObject(f))-t, egyszerűen visszamész a függvényen, és ismét eléri azt a pontot. Ugyanez vonatkozik f (Array.isArray(f)&&Array.isArray(s)) 28.11.2017
  • Új anyagok

    A rádiógomb ellenőrzött eseményének használata a jQueryben
    Ebben a cikkben látni fogjuk, hogyan kell dolgozni a jquery választógombbal ellenőrzött eseményeivel. A választógombok HTML gombok, amelyek segítenek kiválasztani egyetlen értéket egy csoportból...

    Körkörös függőségek megoldása terraformban adatforrásokkal – lépésről lépésre
    Mi az a körkörös függőségek Dolgozzunk egy egyszerű eseten, amikor az SQS-sor és az S3-vödör közötti körkörös függőség problémája van egy egymástól függő címkeérték miatt. provider..

    Miért érdemes elkezdeni a kódolást 2023-ban?
    01100011 01101111 01100100 01100101 — beep boop beep boop Világunk folyamatosan fejlődik a technológia körül, és naponta fejlesztenek új technológiákat a valós problémák megoldására. Amint..

    🎙 Random Noise #2  – Örökbefogadás és hit
    az analitika íratlan világának gondozása Szeretné, hogy ezek a frissítések a postaládájába kerüljenek? Iratkozzon fel itt . "Ha önvezető autókat gyártanak, akkor mi miért ne..

    A legrosszabb politika és prediktív modellek májátültetésre jelöltek számára az Egyesült Államokban
    A máj (vagy óangolul lifer) az emberi test legnehezebb belső szervére utal, amely csendesen működik a nap 24 órájában. Mit csinál a máj? 500 feladatot hajt végre a szervezet egészségének..

    5 webhely, amely 2022-ben fejleszti front-end fejlesztői készségeit
    Frontendmentor.io A tényleges projektek létrehozásával a Frontendmentor.io segítséget nyújt a front-end kódolási képességeinek fejlesztésében. A kódolást azután kezdheti meg, hogy..

    Mikor kell használni a Type-t az interfészhez képest a TypeScriptben?
    A TypeScript a JavaScript gépelt szuperkészlete, amely statikus gépelést ad a nyelvhez. Ez megkönnyíti a robusztus és karbantartható kód írását azáltal, hogy a hibákat a fordítási időben..


    © 2024 webhu.ru, WebHU - Programozási kérdések és válaszok