web-dev-qa-db-fra.com

Comment comparer deux objets et obtenir des paires clé-valeur de leurs différences?

J'ai deux objets:

1)

{A: 10, B: 20, C: 30}

2)

{A: 10, B: 22, C: 30}

comme vous pouvez le voir: il y a presque des égaux, sauf une chose: la clé B valeur est différente.

Comment puis-je entrer dans mes différences someNewArr clé-valeur?

comme someNewArr: {B: 22} (j'obtiens les valeurs du deuxième objet)

j'utilise angular, et je veux dire quelque chose comme ça:

    var compareTwoObjects = function(initialObj, editedObj) {
        var resultArr = [];
        angular.forEach(initialObj, function(firstObjEl, firstObjInd) {
            angular.forEach(editedObj, function(secondObjEl, secondObjInd) {
                if (firstObjEl.key === secondObjEl.key && firstObjEl.value !== secondObjEl.value){
                    resultArr.Push({firstObjEl.key: secondObjEl.value});
                }
            })
        });
    });
7
brabertaser19

diff récursif

Presque trois ans plus tard, je suis heureux de fournir une réponse actualisée à cette question.

Nous commençons avec deux objets différents

const x =
  { a: 1, b: 2, c: 3 }

const y =
  { a: 1, b: 3, d: 4 }

console.log (diff (x, y))
// => ???

Les deux objets ont la même propriété aname__. La propriété bn'est pas la même. Seul xa une propriété cet seul ya une propriété dname__. Alors, que devrait ??? être exactement?

Du point de vue de diffname__, la relation entre nos objets d’entrée aet bpourrait être complètement arbitraire. diffattribue des descripteurs leftet rightname__

console.log (diff (x, y))
// { b: { left: 2, right: 3 }, c: { left: 3 }, d: { right: 4 } }

Dans la sortie ci-dessus, nous pouvons voir

  • quelles propriétés sont différentes - bname__, cet dname__
  • quel objet a contribué à la différence - leftet/ou rightname__
  • la valeur "différente" - par exemple, la gauche ba la valeur 2, la droite ba la valeur 3; ou la gauche ca une valeur de 3, la droite ca une valeur de non défini

Avant de commencer à implémenter cette fonction, nous allons d'abord examiner un scénario plus complexe impliquant des objets profondément imbriqués.

const x =
  { a: { b: { c: 1, d: 2, e: 3 } } }

const y =
  { a: { b: { c: 1, d: 3, f: 4 } } }

console.log (diff (x, y))
// { a: { b: { d: { left: 2, right: 3 }, e: { left: 3 }, f: { right: 4 } } } }

Comme nous pouvons le voir ci-dessus, diffrenvoie une structure qui correspond à nos entrées. Et enfin, nous nous attendons à ce que diffde deux objets identiques renvoie un résultat "vide"

const x1 =
  { a: 1, b: { c: { d: 2 } } }

const x2 =
  { a: 1, b: { c: { d: 2 } } }

console.log (diff (x1, x2))
// {}

Nous décrivons ci-dessus une fonction diffqui ne se soucie pas des objets d'entrée qui lui sont donnés L'objet "gauche" peut contenir des clés que l'objet "droite" ne contient pas, et inversement, nous devons néanmoins détecter les modifications des deux côtés. À partir d'un niveau élevé, voici comment nous aborderons le problème

const diff = (x = {}, y = {}) =>
  merge
    ( diff1 (x, y, "left")
    , diff1 (y, x, "right")
    ) 

diff1

Nous prenons un diff "unilatéral" en utilisant diff1 décrit comme la relation "gauche", et nous prenons un autre diff unilatéral avec les objets en entrée inversés décrits comme la relation "droite", puis nous mergeles deux résultats ensemble

Notre travail est divisé pour nous en tâches plus faciles à accomplir maintenant. diff1 n'a besoin que de détecter la moitié des modifications nécessaires et mergecombine simplement les résultats. Nous allons commencer par diff1

const empty =
  {}

const isObject = x =>
  Object (x) === x

const diff1 = (left = {}, right = {}, rel = "left") =>
  Object.entries (left)
    .map
      ( ([ k, v ]) =>
          isObject (v) && isObject (right[k])
            ? [ k, diff1 (v, right[k], rel) ]
            : right[k] !== v
              ? [ k, { [rel]: v } ]
              : [ k, empty ]
      )
    .reduce
      ( (acc, [ k, v ]) =>
          v === empty
            ? acc
            : { ...acc, [k]: v }
      , empty
      )

diff1 accepte deux objets d'entrée et un descripteur de relation, relname__. La valeur par défaut de ce descripteur est "left", qui est "l'orientation" par défaut de la comparaison. Notez ci-dessous que diff1 ne fournit que la moitié du résultat dont nous avons besoin. Inverser les arguments dans un appel seconde à diff1 fournit l'autre moitié.

const x =
  { a: 1, b: 2, c: 3 }

const y =
  { a: 1, b: 3, d: 4 }

console.log (diff1 (x, y, "left"))
// { b: { left: 2 }, c: { left: 3 } }

console.log (diff1 (y, x, "right"))
// { b: { right: 3 }, d: { right: 4 } }

Il convient également de noter que les étiquettes de relation "left" et "right" sont définissables par l'utilisateur. Par exemple, si vous avez une relation connue entre les objets que vous comparez et que vous souhaitez fournir des étiquettes plus descriptives dans la sortie diff ...

const customDiff = (original = {}, modified = {}) =>
  merge
    ( diff1 (x, y, "original")
    , diff1 (y, x, "modified")
    )

customDiff
    ( { Host: "localhost", port: 80 }
    , { Host: "127.0.0.1", port: 80 }
    )
// { Host: { original: 'localhost', modified: '127.0.0.1' } }

Dans l'exemple ci-dessus, il peut être plus facile de travailler avec la sortie dans d'autres zones de votre programme car les étiquettes originalet modifiedsont plus descriptives que leftet rightname__.

fusionner

Il ne reste plus qu'à fusionner les deux demi-différences pour obtenir un résultat complet. Notre fonction mergefonctionne également de manière générique et accepte deux objets quelconques en entrée.

const x =
  { a: 1, b: 1, c: 1 }

const y =
  { b: 2, d: 2 }

console.log (merge (x, y))
// { a: 1, b: 2, c: 1, d: 2 }

Si chaque objet contient une propriété dont la valeur est aussi un objet, mergese reproduira et fusionnera également les objets imbriqués.

const x =
  { a: { b: { c: 1, d: 1 } } }

const y =
  { a: { b: { c: 2, e: 2 } }, f: 2 }

console.log (merge (x, y))
// { a: { b: { c: 2, d: 1, e: 2 } }, f: 2 }

Ci-dessous, nous codons nos intentions dans mergename__

const merge = (left = {}, right = {}) =>
  Object.entries (right)
    .reduce
      ( (acc, [ k, v ]) =>
          isObject (v) && isObject (left [k])
            ? { ...acc, [k]: merge (left [k], v) }
            : { ...acc, [k]: v }
      , left
      )

Et c'est tout le kit et caboodle! Développez l'extrait de code ci-dessous pour exécuter une démonstration de code dans votre propre navigateur.

const empty =
  {}

const isObject = x =>
  Object (x) === x

const diff1 = (left = {}, right = {}, rel = "left") =>
  Object.entries (left)
    .map
      ( ([ k, v ]) =>
          isObject (v) && isObject (right[k])
            ? [ k, diff1 (v, right[k], rel) ]
            : right[k] !== v
              ? [ k, { [rel]: v } ]
              : [ k, empty ]
      )
    .reduce
      ( (acc, [ k, v ]) =>
          v === empty
            ? acc
            : { ...acc, [k]: v }
      , empty
      )

const merge = (left = {}, right = {}) =>
  Object.entries (right)
    .reduce
      ( (acc, [ k, v ]) =>
          isObject (v) && isObject (left [k])
            ? { ...acc, [k]: merge (left [k], v) }
            : { ...acc, [k]: v }
      , left
      )

const diff = (x = {}, y = {}) =>
  merge
    ( diff1 (x, y, "left")
    , diff1 (y, x, "right")
    )

const x =
  { a: { b: { c: 1, d: 2, e: 3 } } }

const y =
  { a: { b: { c: 1, d: 3, f: 4 } } }

console.log (diff (x, y))
// { a: { b: { d: { left: 2, right: 3 }, e: { left: 3 }, f: { right: 4 } } } }

console.log (diff (diff (x,y), diff (x,y)))
// {} 

remarques

Lorsque nous examinons notre fonction diffname__, je souhaite mettre en évidence une partie importante de sa conception. Une bonne partie du travail est gérée par la fonction mergename__, qui est complètement distincte de diffname__, et pourtant, un ough nut to crack à lui tout seul. Puisque nous avons séparé nos préoccupations en fonctions uniques, il est maintenant facile de les réutiliser dans d’autres domaines de votre programme. Où nous voulions diffname__, nous l'avons, et nous avons la fonctionnalité intuitive intuitive mergegratuitement.


extra: support pour les tableaux

Notre fonction diffest très pratique car elle peut analyser des objets profondément imbriqués, mais que se passe-t-il si l'une de nos propriétés d'objet est un tableau? Ce serait bien si nous pouvions différer les tableaux en utilisant la même technique.

La prise en charge de cette fonctionnalité nécessite des modifications non triviales du code ci-dessus. Cependant, la majorité de la structure et du raisonnement restent les mêmes. Par exemple, diffest totalement inchangé.

// unchanged
const diff = (x = {}, y = {}) =>
  merge
    ( diff1 (x, y, "left")
    , diff1 (y, x, "right")
    )

Pour prendre en charge les tableaux de mergename__, nous introduisons un assistant de mutation mutqui attribue une paire [ key, value ] à un objet donné, oname__. Les tableaux sont également considérés comme des objets. Nous pouvons donc mettre à jour les tableaux et les objets en utilisant la même fonction mutname__.

const mut = (o, [ k, v ]) =>
  (o [k] = v, o)

const merge = (left = {}, right = {}) =>
  Object.entries (right)
    .map
      ( ([ k, v ]) =>
          isObject (v) && isObject (left [k])
            ? [ k, merge (left [k], v) ]
            : [ k, v ]
      )
    .reduce (mut, left)

Peu profonde fusionne le travail comme prévu

const x =
  [ 1, 2, 3, 4, 5 ]

const y =
  [ 0, 0, 0 ]

const z =
  [ , , , , , 6 ]

console.log (merge (x, y))
// [ 0, 0, 0, 4, 5 ]

console.log (merge (y, z))
// [ 0, 0, 0, <2 empty items>, 6 ]

console.log (merge (x, z))
// [ 1, 2, 3, 4, 5, 6 ]

Et la fusion profonde aussi

const x =
  { a: [ { b: 1 }, { c: 1 } ] }

const y =
  { a: [ { d: 2 }, { c: 2 }, { e: 2 } ] }

console.log (merge (x, y))
// { a: [ { b: 1, d: 2 }, { c: 2 }, { e: 2 } ] }

La prise en charge des tableaux dans diff1 est considérablement plus complexe

const diff1 = (left = {}, right = {}, rel = "left") =>
  Object.entries (left)
    .map
      ( ([ k, v ]) =>
          isObject (v) && isObject (right[k])
            ? [ k, diff1 (v, right[k], rel) ]
            : right[k] !== v
              ? [ k, { [rel]: v } ]
              : [ k, {} ]
      )
    .filter
      ( ([ k, v ]) =>
          Object.keys (v) .length !== 0
      )
    .reduce
      ( mut
      , isArray (left) && isArray (right) ? [] : {}
      )

Mais avec ces changements en place, nous pouvons maintenant comparer en profondeur les objets contenant des tableaux - et même les tableaux contenant des objets!

const x =
  { a: 1, b: [ { c: 1 }, { d: 1 }, { e: 1 } ] }

const y =
  { a: 1, b: [ { c: 2 }, { d: 1 }, 5, 6 ], z: 2 }

console.log (diff (x, y))
// { b:
//     [ { c: { left: 1, right: 2 } }
//     , <1 empty item>
//     , { left: { e: 1 }, right: 5 }
//     , { right: 6 }
//     ]
// , z: { right: 2 } 
// }

Étant donné que diff1 modifie soigneusement son comportement en fonction de ses types d'entrée, nous obtenons un tableau différent gratuitement

const x =
  [ 1, 2, 3, 4 ]

const y =
  [ 1, 2, 9 ]

const z =
  [ 1, 2, 9 ]

console.log (diff (x, y))
// [ <2 empty items>, { left: 3, right: 9 }, { left: 4 } ]

console.log (diff (y, z))
// []

Exécutez le programme complet dans votre navigateur ci-dessous

const isObject = x =>
  Object (x) === x

const isArray =
  Array.isArray

const mut = (o, [ k, v ]) =>
  (o [k] = v, o)

const diff1 = (left = {}, right = {}, rel = "left") =>
  Object.entries (left)
    .map
      ( ([ k, v ]) =>
          isObject (v) && isObject (right[k])
            ? [ k, diff1 (v, right[k], rel) ]
            : right[k] !== v
              ? [ k, { [rel]: v } ]
              : [ k, {} ]
      )
    .filter
      ( ([ k, v ]) =>
          Object.keys (v) .length !== 0
      )
    .reduce
      ( mut
      , isArray (left) && isArray (right) ? [] : {}
      )

const merge = (left = {}, right = {}) =>
  Object.entries (right)
    .map
      ( ([ k, v ]) =>
          isObject (v) && isObject (left [k])
            ? [ k, merge (left [k], v) ]
            : [ k, v ]
      )
    .reduce (mut, left)


const diff = (x = {}, y = {}) =>
  merge
    ( diff1 (x, y, "left")
    , diff1 (y, x, "right")
    )

const x =
  { a: 1, b: [ { c: 1 }, { d: 1 }, { e: 1 } ] }

const y =
  { a: 1, b: [ { c: 2 }, { d: 1 }, 5, 6 ], z: 2 }

console.log (diff (x, y))
// { b:
//     [ { c: { left: 1, right: 2 } }
//     , <1 empty item>
//     , { left: { e: 1 }, right: 5 }
//     , { right: 6 }
//     ]
// , z: { right: 2 } 
// }

peu profonde diff

La version précédente de cette réponse fournissait une fonction objet diffpermettant de comparer des objets avec les mêmes clés et de comparer des objets avec des clés différentes, mais aucune des deux solutions ne procédait de manière récursive aux objets imbriqués.

union récursive

Dans this Q & R , nous prenons deux objets d'entrée et calculons un unionrécursif au lieu d'un diffname__

16
user633183

Cette solution n'est pas angulaire, mais cela pourrait aider.

Il faudra 2 objets avec un nombre quelconque de clés, et ils n'ont pas besoin de contenir les mêmes clés.

** Sortie: ** The key:value pairs which are present in only one object and not the other and the key:value pairs which are present in both objects but the values are different.

var obj1 = {A: 10, B: 20, C: 30, E: 40};
var obj2 = {A: 11, B: 20, C: 30, D: 50};
var finalObject = {};

$( document ).ready(function() {
    var keysOfObj1 = Object.keys( obj1 );
    var keysOfObj2 = Object.keys( obj2 );

    var keys = [];
    keys = $( keysOfObj1 ).not( keysOfObj2 ).get(); // keys of first object not in second object

    for( var i=0;i<keys.length;i++ ) {
        finalObject[ keys[ i ] ] = obj1[ keys[ i ] ];
    }

    keys.length = 0; // reset the temp array

    keys = $( keysOfObj2 ).not( keysOfObj1 ).get(); // keys of second object not in first object

    for( var i=0;i<keys.length;i++ ) {
        finalObject[ keys[ i ] ] = obj2[ keys[ i ] ];
    }

    keys.length = 0; // reset the temp array again

    if( keysOfObj1.length != keysOfObj2.length ) {
        // case already handled above
    }

    for( var i in obj1 ) {
        if( obj1.hasOwnProperty( i ) ) {
            if( obj2.hasOwnProperty( i ) ) {
                if( obj1[ i ] != obj2[ i ] ) {
                    finalObject[ i ] = obj2[ i ];
                } else {
                    // the property has the same value in both objects, all is well...
                }
            } else {
                // case already handled above
            }
        } else {
            // case already handled above
        }
    }
    console.log( obj1 );
    console.log( obj2 );
    console.log( finalObject );

J'espère que ça aide.

0
web-nomad

La solution est assez simple,

Initialisez votre tableau,

var resultArray = [];

parcourez ensuite les clés de votre objet, en en utilisant une comme référence (en supposant que les objets possèdent les mêmes clés, mais que vous souhaitiez vérifier les clés contenant des valeurs différentes)

et enfin exécuter le code simple

for(let key in obj){
    // console.log(key);
    if(obj[key]  !== this.profileObject[key] ){
      resultArray.Push(key);
    }
}

Et récupère ta réponse à la fin

console.log(resultArray);
0
theProgrammer

Cela retournera le diff du premier argument par rapport au second argument. Je n'utilise pas angular.forEach ici cependant.

var x = {
   a : 1,
   b:2,
  c :3,
  d:4
 }

var y = {
   a : 1,
   b:4,
  c :3,
  d : 5
 };


var diff = function(x,y){
  var target = {};   
  var diffProps = Object.keys(x).filter(function(i){
    if(x[i] !== y[i]){
      return true;
    }
    return false;
   }).map(function(j){
       var obj = {};
       obj[j] = x[j];
       target = Object.assign(target,obj)
  });
   return target;
};


console.log(diff(x,y));
0
Bhargav Ponnapalli

J'espère que cela t'aidera. Je l'ai fait avec la fonction jQuery each.

var a = {A: 10, B: 20, C: 30};
var b = {A: 10, B: 22, C: 30};
var hasObj = false; //Declaring variable outside for onetime memory allocation.

$.each(b, function(keyOfB, valOfB) {

    hasObj = false;  //Assigning false for each parent loop

    $.each(a, function(keyOfA, valOfA) {
        if (keyOfA == keyOfB && valOfA == valOfB) {
            hasObj = true;
            return false; //If key and value mathed loop will break and no remaining items of second array will be check.
        }
    });

    if (hasObj == false) {
        console.log(keyOfB + "--" + valOfB); //Printing the unmatched key and value
    }

});
0
Mehmood
$scope.ar1 = {A: 10, B: 20, C: 30};

$scope.ar2 = {A: 10, B: 22, C: 30};

$scope.newObj = {};
angular.forEach($scope.ar1, function(v, i) {
    // if ar2[i] is exists and ar2[i] != v then put that value to newObj
    if ($scope.ar2[i] && $scope.ar2[i] != v) {
        $scope.newObj[i] = $scope.ar2[i];
    }
});

console.log($scope.newObj);

voici la DEMO

0
K.Toress