Сравнение объектов
Собственно вопрос был озвучен в названии темы.
Я так полагая что штатные операторы сравнения == не будут правильно работать и нужно писать свою рекурсивною функцию для сравнения свойств объектов. Может есть готовые решения или это можно сделать стандартными возможностями языка? |
Нету стандартных средств, ни сравнения, ни копирования.
|
А можно узнать, где вам это понадобилось?
|
Есть хешовый объект, в котором хранятся настройки. Во время нахождения пользователя на сайте, некоторые свойства от настроек могут изменится. Если какие-то свойства изменились, то я передаю объект на сервер через ajax.
Я лично отталкивался от такой логики: 1) получаем дефолтный хеш настроек. 2) клонируем его в другой хеш (функцию клонирования уже отыскал в сети) 3) используем функцию обновления данных в хеше, если нужно. 4) в самом конце сравниваем начальный хеш с конечным ,если изменился, тогда передаю на сервер данные. Весь смысл моего извращения, чтобы не дергать каждый раз запрос на сервер ,а отсылать его тогда, когда реально были изменены данные. Вот. Хотелось бы услышать ваши мысли, вдруг моя логика хромает и есть варианты попроще. |
Цитата:
|
Cepin, ну а если в массив, а там уже сравнивать, проверять каждое значение на совпадение или несовпадение?
|
Я пишу userscript. И данные которые будут менятся, изначально мне недоступны, они выдаются сервером..но не каждый раз, а примерно через 40 минут или через день. Затем мне нужно хеш с настройками передать на сервер.
|
Но меняет-то их ваш код?
|
Нет, мой код ничего не меняет. Он просто использует настройки и если есть необходимость, он их обновляет. Это все идет как внешнее дополнение. Поэтому я придерживаюсь позиции: если обновились данные, тогда их следует обновить. Вообщем по сути, писать свою функцию сравнения, верно?
|
Вы же их как-то сериализируете перед отправкой на сервер. Вот и сравнивайте строки, а не объекты.
|
Спасибо. Все гениальное просто) Благодарю.
|
Object.prototype.valueOf= function(){ return JSON.stringify( this ) }
alert( {a:{b:2}} == {a:{b:2}} ) |
Метод конечно простой и удобный, но ...
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} |
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]}})); |
Немного доработал предыдущий вариант, если кому интересно.
/** сравнить объекты по значению рекурсивно все свойства. *@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 |
tenshi, же написал
Object.prototype.valueOf= function(){ return JSON.stringify( this ) } alert( {a:{b:2}} == {a:{b:2}} ) |
|
Nekromancer, особо не вглядывался, но при сравнении объектов с "бесконечными" ссылками не зависает из-за рекурсии?
пример "бесконечной" ссылки var obj = { a: some_value, b: obj }; |
Ну наверно зависнет, да :) Надо поставить ограничение наверно. Я этот метод не использовал, кажется, просто написал.
|
Ну, от рекурсии при сериализации избавится просто - составлять по мере исследования массив объектов.
Но для сравнения этого скорее всего не нужно. |
можно сравнивать по ссылкам на один обьект они ведут или нет)
|
Это не тут случай. Речь идёт о разных объектах, которые парсятся из строки ну или к примеру просто из разных мест.
|
Не, я про рекурcии говорю, про анализ их, можно же стеком делать это. и првеорять идем мы по одной и той же ветви второй раз или нет) это же просто
|
Примерно так(хотя мои представления о сравнении немого отличаются от оных предыдущего постера:)) :
(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 ) |
Я решил это так jQuery или Zepto на чистом будет сложнее
$.map(obj1, function(v, k) { return obj2[k] && obj2[k] == v ? true : false; }).indexOf(false) < 0
|
Часовой пояс GMT +3, время: 18:27. |