Javascript-форум (https://javascript.ru/forum/)
-   Общие вопросы Javascript (https://javascript.ru/forum/misc/)
-   -   Сравнение объектов (https://javascript.ru/forum/misc/10792-sravnenie-obektov.html)

Cepin 21.07.2010 10:43

Сравнение объектов
 
Собственно вопрос был озвучен в названии темы.
Я так полагая что штатные операторы сравнения == не будут правильно работать и нужно писать свою рекурсивною функцию для сравнения свойств объектов.

Может есть готовые решения или это можно сделать стандартными возможностями языка?

Octane 21.07.2010 10:48

Нету стандартных средств, ни сравнения, ни копирования.

Kolyaj 21.07.2010 10:53

А можно узнать, где вам это понадобилось?

Cepin 21.07.2010 11:00

Есть хешовый объект, в котором хранятся настройки. Во время нахождения пользователя на сайте, некоторые свойства от настроек могут изменится. Если какие-то свойства изменились, то я передаю объект на сервер через ajax.

Я лично отталкивался от такой логики:
1) получаем дефолтный хеш настроек.
2) клонируем его в другой хеш (функцию клонирования уже отыскал в сети)
3) используем функцию обновления данных в хеше, если нужно.
4) в самом конце сравниваем начальный хеш с конечным ,если изменился, тогда передаю на сервер данные.

Весь смысл моего извращения, чтобы не дергать каждый раз запрос на сервер ,а отсылать его тогда, когда реально были изменены данные.

Вот. Хотелось бы услышать ваши мысли, вдруг моя логика хромает и есть варианты попроще.

Kolyaj 21.07.2010 11:03

Цитата:

Сообщение от Cepin
Если какие-то свойства изменились, то я передаю объект на сервер через ajax.

В какой момент?

Vulkan 21.07.2010 11:04

Cepin, ну а если в массив, а там уже сравнивать, проверять каждое значение на совпадение или несовпадение?

Cepin 21.07.2010 11:07

Я пишу userscript. И данные которые будут менятся, изначально мне недоступны, они выдаются сервером..но не каждый раз, а примерно через 40 минут или через день. Затем мне нужно хеш с настройками передать на сервер.

Kolyaj 21.07.2010 11:10

Но меняет-то их ваш код?

Cepin 21.07.2010 11:21

Нет, мой код ничего не меняет. Он просто использует настройки и если есть необходимость, он их обновляет. Это все идет как внешнее дополнение. Поэтому я придерживаюсь позиции: если обновились данные, тогда их следует обновить. Вообщем по сути, писать свою функцию сравнения, верно?

Kolyaj 21.07.2010 11:28

Вы же их как-то сериализируете перед отправкой на сервер. Вот и сравнивайте строки, а не объекты.

Cepin 21.07.2010 11:30

Спасибо. Все гениальное просто) Благодарю.

tenshi 25.07.2010 14:47

Object.prototype.valueOf= function(){ return JSON.stringify( this ) }

alert( {a:{b:2}} == {a:{b:2}} )

Ivan Galin 17.06.2011 19:25

Метод конечно простой и удобный, но ...
var o1 = {a:1,b:2}, s1 = JSON.stringify(o1),
    o2 = {b:2,a:1}, s2 = JSON.stringify(o2);
console.log(s1 == s2);            //  false
console.log(s1);                  // {"a":1,"b":2}
console.log(s2);                  // {"b":2,"a":1}

nikita.mmf 20.06.2011 13:41

Object.equal = function( firstObj, secondObject ){
	var keysFirstObj = Object.keys( firstObj );
	var keysSecondObject = Object.keys( secondObject );
	if ( keysFirstObj.length != keysSecondObject.length ) {
		return false;
	}
	return !keysFirstObj.filter(function( key ){
		if ( typeof firstObj[key] == "object" ||  Array.isArray( firstObj[key] ) ) {
			return !Object.equal(firstObj[key], secondObject[key]);
		} else {
			return firstObj[key] !== secondObject[key];
		}
	}).length;
}
alert(Object.equal({a:[1,2,{b:1}], c: {d:[1,2,3]}},{a:[1,2,{b:1}], c: {d:[1,2,3]}}));

andrewinc 11.10.2012 00:24

Немного доработал предыдущий вариант, если кому интересно.

/** сравнить объекты по значению рекурсивно все свойства.
 *@param Object firstObj один из объектов для сравнения
 *@param Object secondObject другой объект для сравнения
 *@return boolean true одинаковы, false - различаются
 *Пр.: Object.equals({a:[1,2,3],b:2}, {b:2,a:[1,2,3]}); //true
 *     Object.equals(null, null); //false
 *     Object.equals(1, 1); //true
 *     Object.equals(1, '1');//true
 *     Object.equals(1, {a:1});//false
 *     Object.equals([1,2], [1,2]);//true
 *     Object.equals(new Date(), new Date())//true
 *     
 *     var d= new Date();
 *     var d2= new Date(d.getTime()+3600000);
 *     Object.equals(d, d2); //false
 *     Object.equals(d, d); //true
 *     Object.equals(d, new Date(d.getTime())); //true
 **/
Object.equals = function( firstObj, secondObject ){
    if ((null==firstObj)||(null==secondObject)) return false; //null сравниваем правильно. Если хоть в одном операнде значит false
    if (('object'!=typeof firstObj) && ('object'!=typeof secondObject))  return firstObj==secondObject;//оба из простых - сравнение простых значений. 
    else if (('object'!=typeof firstObj) || ('object'!=typeof secondObject)) return false;//сравнивается простое значение с объектом - всегда не равны
    //в этой точке только если сравниваем именно объекты (оба)    
    
    //отдельно для дат
    if ((firstObj instanceof Date) && (secondObject instanceof Date)) return firstObj.getTime()==secondObject.getTime(); //исключение для дат. сравним их по микросекундам
    else if ((firstObj instanceof Date) && (secondObject instanceof Date)) return false;//Если дата сравнивается с чем-то ещё -то всегда выдать несоответствие
    
    //далее сравниваем нормальные объекты
    var keysFirstObj = Object.keys( firstObj );
    var keysSecondObject = Object.keys( secondObject );
    if ( keysFirstObj.length != keysSecondObject.length ) {return false;}
    return !keysFirstObj.filter(function( key ){
        if ( typeof firstObj[key] == "object" ||  Array.isArray( firstObj[key] ) ) {
            return !Object.equals(firstObj[key], secondObject[key]);
        } else {
            return firstObj[key] !== secondObject[key];
        }
    }).length;
}

alert(Object.equals({a:[1,2,3],b:2}, {b:2,a:[1,2,3]})); //true
alert(Object.equals(null, null); //false
alert(Object.equals(1, 1); //true
alert(Object.equals(1, '1');//true
alert(Object.equals(1, {a:1});//false
alert(Object.equals([1,2], [1,2]);//true
alert(Object.equals(new Date(), new Date())//true

Maxmaxmахimus 11.10.2012 00:31

tenshi, же написал


Object.prototype.valueOf= function(){ return JSON.stringify( this ) }

alert( {a:{b:2}} == {a:{b:2}} )

Nekromancer 11.10.2012 00:43

https://github.com/NekR/Sync/blob/master/core.js#L248

nerv_ 11.10.2012 01:08

Nekromancer, особо не вглядывался, но при сравнении объектов с "бесконечными" ссылками не зависает из-за рекурсии?

пример "бесконечной" ссылки

var obj = {
    a: some_value,
    b: obj
};

Nekromancer 11.10.2012 01:18

Ну наверно зависнет, да :) Надо поставить ограничение наверно. Я этот метод не использовал, кажется, просто написал.

Aetae 11.10.2012 01:40

Ну, от рекурсии при сериализации избавится просто - составлять по мере исследования массив объектов.
Но для сравнения этого скорее всего не нужно.

Maxmaxmахimus 11.10.2012 01:47

можно сравнивать по ссылкам на один обьект они ведут или нет)

Nekromancer 11.10.2012 01:51

Это не тут случай. Речь идёт о разных объектах, которые парсятся из строки ну или к примеру просто из разных мест.

Maxmaxmахimus 11.10.2012 01:58

Не, я про рекурcии говорю, про анализ их, можно же стеком делать это. и првеорять идем мы по одной и той же ветви второй раз или нет) это же просто

Aetae 11.10.2012 04:29

Примерно так(хотя мои представления о сравнении немого отличаются от оных предыдущего постера:)) :
(function(a){ //расширение алерта, для вывода нескольких значений(мне так нравится=))
	window.alert = function(){
		a(Array.prototype.slice.call(arguments).join('\n'))
	}
}(window.alert))
 
 
if (!Object.keys) { //Object.keys для ослов(с MDN)
  Object.keys = (function () {
    var hasOwnProperty = Object.prototype.hasOwnProperty,
        hasDontEnumBug = !({toString: null}).propertyIsEnumerable('toString'),
        dontEnums = [
          'toString',
          'toLocaleString',
          'valueOf',
          'hasOwnProperty',
          'isPrototypeOf',
          'propertyIsEnumerable',
          'constructor'
        ],
        dontEnumsLength = dontEnums.length
 
    return function (obj) {
      if (typeof obj !== 'object' && typeof obj !== 'function' || obj === null) throw new TypeError('Object.keys called on non-object')
 
      var result = []
 
      for (var prop in obj) {
        if (hasOwnProperty.call(obj, prop)) result.push(prop)
      }
 
      if (hasDontEnumBug) {
        for (var i=0; i < dontEnumsLength; i++) {
          if (hasOwnProperty.call(obj, dontEnums[i])) result.push(dontEnums[i])
        }
      }
      return result
    }
  })()
};



 
Object.equals = function( first, second ){
	
	var cache = []; //кеш обьектов, для избежания рекурсии
	
	function inCache( first, second ){
		var i = cache.length;
		while(i--) if( 
			( cache[i][0] === first || cache[i][0] === second) && (cache[i][1] === second  || cache[i][1] === first ) 
		) return true;
		return false
	}
	
	return function eq(f, s){
		if ( f === s ) return true; //сравниваем обычным образом
		if ( f instanceof Date && s instanceof Date ) return +f === +s; //время
		if ( typeof f !== 'object' || typeof s !== 'object' ) return false; //если хотябы один из аргументов не объект (положительный случай для необъектов рассмотрен выше)
		if( inCache(f, s) ) return true; //есть в кеше
		cache.push( [f , s] ); //кешируем
		
		var keys = Object.keys(f), i = keys.length; //получаем ключи
		if (Object.keys(s).length !== i ) return false; //если количество ключей не совпадает
		while(i--) if( !eq(f[keys[i]], s[keys[i]]) ) return false; //рекурсивный вызов
		
		return true
	}(first, second)
}

alert(
	'Обычные объекты:',
	Object.equals({a:[1,2,3],b:2}, {b:2,a:[1,2,3]}), //true
	Object.equals(null, null), //true
	Object.equals(1, 1), //true
	Object.equals(1, '1'), //false
	Object.equals(1, {a:1}), //false
	Object.equals([1,2], [1,2]), //true
	Object.equals(new Date(), new Date()) //true
)

a = {x:1,c:{r:0}};
b = {x:1,c:{r:0}};
c = {x:1,c:{r:0}};
a.c.r = a;
b.c.r = b;
c.c.r = c;
a.z = b;
b.z = c;
c.z = a;
alert( 
	'Рекурсивные:',
	Object.equals(a, b), //true
	Object.equals(a, c), //true
	Object.equals(c, b), //true
	Object.equals(a, {x:1,c:{r:''}}) //false
)

LEV7n 21.03.2016 12:09

Я решил это так jQuery или Zepto на чистом будет сложнее
 
$.map(obj1, function(v, k) { return obj2[k] && obj2[k] == v ? true : false; }).indexOf(false) < 0


Часовой пояс GMT +3, время: 18:27.