จะกำหนดความเท่าเทียมกันสำหรับวัตถุ JavaScript สองตัวได้อย่างไร


670

ตัวดำเนินการความเสมอภาคที่เข้มงวดจะบอกคุณว่าประเภทของวัตถุสองชนิดเท่ากันหรือไม่ อย่างไรก็ตามมีวิธีที่จะบอกได้หรือไม่ว่าวัตถุทั้งสองนั้นมีค่าเท่ากันเหมือนกับค่ารหัสแฮชใน Java หรือไม่

คำถาม Stack Overflow มีฟังก์ชั่น hashCode ใน JavaScript หรือไม่? คล้ายกับคำถามนี้ แต่ต้องการคำตอบเชิงวิชาการเพิ่มเติม สถานการณ์ดังกล่าวข้างต้นแสดงให้เห็นว่าทำไมมันจำเป็นจะต้องมีหนึ่งและฉันสงสัยว่าถ้ามีวิธีการแก้ปัญหาเทียบเท่า


3
นอกจากนี้โปรดดูคำถามนี้stackoverflow.com/q/1068834/1671639
Praveen

37
โปรดทราบว่าแม้ใน Java a.hashCode() == b.hashCode()ไม่ได้หมายความว่าจะมีค่าเท่ากับa bเป็นเงื่อนไขที่จำเป็นไม่เพียงพอ
Heinzi

โปรดดูที่ qs นี้: stackoverflow.com/questions/35933616/…
ลืมไปแล้ว

2
หากคุณต้องเปรียบเทียบวัตถุในรหัสของคุณมากกว่าที่คุณเขียนโค้ดผิด คำถามที่ดีกว่าอาจเป็น: "ฉันจะเขียนรหัสนี้อย่างไรฉันจึงไม่ต้องเปรียบเทียบวัตถุ"
th317erd

3
@ th317erd คุณช่วยอธิบายตัวเองได้มั้ย ...
El Mac

คำตอบ:


175

คำตอบสั้น ๆ

คำตอบง่ายๆคือ: ไม่ไม่มีวิธีการทั่วไปในการพิจารณาว่าวัตถุนั้นมีค่าเท่ากับอีกวัตถุหนึ่งในแง่ที่คุณหมายถึง ข้อยกเว้นคือเมื่อคุณคิดถึงวัตถุที่พิมพ์ไม่ออก

คำตอบที่ยาว

แนวคิดคือวิธีการเท่ากับที่เปรียบเทียบสองอินสแตนซ์ที่แตกต่างกันของวัตถุเพื่อระบุว่าพวกเขาจะเท่ากันในระดับค่า อย่างไรก็ตามมันขึ้นอยู่กับประเภทเฉพาะเพื่อกำหนดวิธีEqualsการใช้งาน การเปรียบเทียบซ้ำของคุณลักษณะที่มีค่าดั้งเดิมอาจไม่เพียงพออาจมีแอตทริบิวต์ที่ไม่ควรพิจารณาเป็นส่วนหนึ่งของค่าวัตถุ ตัวอย่างเช่น,

 function MyClass(a, b)
 {
     var c;
     this.getCLazy = function() {
         if (c === undefined) c = a * b // imagine * is really expensive
         return c;
     }
  }

ในกรณีข้างต้นนี้cไม่สำคัญที่จะตรวจสอบว่าอินสแตนซ์ของ MyClass ใด ๆ สองค่าเท่ากันเท่านั้นaและbมีความสำคัญ ในบางกรณีcอาจแตกต่างกันระหว่างอินสแตนซ์และยังไม่สำคัญระหว่างการเปรียบเทียบ

หมายเหตุปัญหานี้ใช้เมื่อสมาชิกเองอาจเป็นกรณีของประเภทและแต่ละคนจะต้องมีวิธีการกำหนดความเท่าเทียมกัน

สิ่งที่ซับซ้อนกว่านั้นก็คือใน JavaScript ความแตกต่างระหว่างข้อมูลและวิธีการเบลอ

วัตถุอาจอ้างอิงถึงวิธีการที่จะเรียกว่าเป็นตัวจัดการเหตุการณ์และสิ่งนี้อาจไม่ถือว่าเป็นส่วนหนึ่งของ 'รัฐค่า' ในขณะที่วัตถุอื่นอาจได้รับมอบหมายฟังก์ชันที่ทำการคำนวณที่สำคัญและทำให้อินสแตนซ์นี้แตกต่างจากวัตถุอื่นเพียงเพราะมันอ้างอิงฟังก์ชันที่แตกต่างกัน

สิ่งที่เกี่ยวกับวัตถุที่มีหนึ่งในวิธีการต้นแบบที่มีอยู่แทนที่ด้วยฟังก์ชั่นอื่น? มันยังคงได้รับการพิจารณาเท่ากับอินสแตนซ์อื่นที่มันเหมือนกันหรือไม่ คำถามนั้นสามารถตอบได้ในแต่ละกรณีเฉพาะสำหรับแต่ละประเภท

ตามที่ระบุไว้ก่อนหน้านี้ข้อยกเว้นจะเป็นวัตถุที่ไม่มีตัวตนอย่างเคร่งครัด ในกรณีนี้ทางเลือกที่สมเหตุสมผลเพียงอย่างเดียวคือการเปรียบเทียบแบบวนซ้ำและแบบวนซ้ำของสมาชิกแต่ละคน ถึงอย่างนั้นก็ต้องถามว่า 'คุณค่า' ของฟังก์ชั่นคืออะไร?


176
หากคุณใช้เครื่องหมายขีดล่างคุณก็สามารถทำได้_.isEqual(obj1, obj2);
chovy

12
@ รุนแรงคำตอบไม่สามารถแก้ปัญหาใด ๆ เพราะไม่มี แม้จะอยู่ใน Java ไม่มี bullet เงินเพื่อเปรียบเทียบวัตถุเสมอภาคและความถูกต้องใช้.equalsวิธีการที่ไม่น่ารำคาญซึ่งเป็นเหตุผลที่มีหัวข้อดังกล่าวมุ่งมั่นในการมีผลบังคับใช้ Java
lcn

3
@ Kumar Harsh สิ่งที่ทำให้สองวัตถุเท่ากันนั้นเป็นแอปพลิเคชันที่เฉพาะเจาะจงมาก ไม่ควรคำนึงถึงคุณสมบัติของวัตถุทุกชิ้นดังนั้นการบังคับให้คุณสมบัติทุกอย่างของวัตถุนั้นไม่ใช่โซลูชันที่เป็นรูปธรรมเช่นกัน
sethro

googled javascript equality object, มี tl; dr reply, ใช้หนึ่งซับจาก @chovy คอมเม้นต์ ขอบคุณ
Andrea

ถ้าใช้เชิงมุมคุณจะต้องangular.equals
boatcoder

507

ทำไมต้องคิดค้นล้อใหม่ ให้Lodashลอง แต่ก็มีจำนวนของฟังก์ชั่นที่จำเป็นต้องมีเช่นIsEqual ()

_.isEqual(object, other);

มันจะทำการตรวจสอบค่าคีย์แต่ละตัว - เช่นเดียวกับตัวอย่างอื่น ๆ ในหน้านี้โดยใช้ECMAScript 5และการปรับให้เหมาะสมแบบดั้งเดิมหากมีอยู่ในเบราว์เซอร์

หมายเหตุ: ก่อนหน้านี้คำตอบนี้แนะนำUnderscore.jsแต่lodash ทำงานได้ดีขึ้นในการแก้ไขข้อบกพร่องและแก้ไขปัญหาที่มีความสอดคล้อง


27
ฟังก์ชั่น isEqual ของ Underscore นั้นดีมาก (แต่คุณต้องดึงไลบรารี่ของพวกเขาเพื่อใช้งาน - ประมาณ 3K gzipped)
mckoss

29
ถ้าคุณดูสิ่งที่ขีดเส้นใต้ให้คุณคุณจะไม่เสียใจที่ดึงมันเข้ามา
PandaWood

6
แม้ว่าคุณจะไม่สามารถขีดเส้นใต้เป็นผู้พึ่งพาได้ แต่ก็ดึงฟังก์ชั่น isEqual ออกมาตอบสนองความต้องการด้านลิขสิทธิ์และดำเนินการต่อ มันคือการทดสอบความเท่าเทียมที่ครอบคลุมที่สุดที่กล่าวถึงใน stackoverflow
Dale Anderson

7
มีทางแยกของขีดล่างที่เรียกว่าLoDashและผู้เขียนนั้นมีความกังวลอย่างมากกับปัญหาความมั่นคงเช่นนั้น ทดสอบกับ LoDash และดูว่าคุณได้อะไร
CoolAJ86

6
@mckoss คุณสามารถใช้โมดูลสแตนด์อะโลนได้หากคุณไม่ต้องการไลบรารีทั้งหมดnpmjs.com/package/lodash.isequal
Rob Fox

161

ตัวดำเนินการความเท่าเทียมกันเริ่มต้นใน JavaScript สำหรับวัตถุให้ผลเป็นจริงเมื่ออ้างถึงตำแหน่งเดียวกันในหน่วยความจำ

var x = {};
var y = {};
var z = x;

x === y; // => false
x === z; // => true

หากคุณต้องการโอเปอเรเตอร์ความเสมอภาคที่แตกต่างกันคุณจะต้องเพิ่มequals(other)วิธีการหรือสิ่งที่ชอบให้กับคลาสของคุณและข้อมูลเฉพาะของโดเมนปัญหาของคุณจะเป็นตัวกำหนดความหมายที่แท้จริง

นี่คือตัวอย่างการ์ดที่เล่น:

function Card(rank, suit) {
  this.rank = rank;
  this.suit = suit;
  this.equals = function(other) {
     return other.rank == this.rank && other.suit == this.suit;
  };
}

var queenOfClubs = new Card(12, "C");
var kingOfSpades = new Card(13, "S");

queenOfClubs.equals(kingOfSpades); // => false
kingOfSpades.equals(new Card(13, "S")); // => true

หากวัตถุสามารถถูกแปลงเป็นสตริง JSON มันจะทำให้ฟังก์ชั่นเท่ากับ () ง่าย
scotts

3
@scotts ไม่เสมอไป การแปลงออบเจ็กต์เป็น JSON และการเปรียบเทียบสตริงสามารถกลายเป็นชนิดที่คำนวณได้สำหรับวัตถุที่ซับซ้อนในลูปที่แคบ สำหรับวัตถุธรรมดามันอาจไม่สำคัญมากนัก แต่ในความเป็นจริงมันขึ้นอยู่กับสถานการณ์เฉพาะของคุณ วิธีการแก้ไขที่ถูกต้องอาจง่ายพอ ๆ กับการเปรียบเทียบ ID ของวัตถุหรือการตรวจสอบแต่ละคุณสมบัติ แต่ความถูกต้องนั้นถูกกำหนดโดยโดเมนปัญหาทั้งหมด
Daniel X Moore

เราไม่ควรเปรียบเทียบประเภทข้อมูลด้วย! return other.rank === this.rank && other.suit === this.suit;
devsathish

1
@devathathish อาจไม่ ในประเภท JavaScript นั้นค่อนข้างรวดเร็วและหลวม แต่ถ้าในประเภทโดเมนของคุณมีความสำคัญคุณอาจต้องการตรวจสอบประเภทเช่นกัน
Daniel X Moore

7
@scotts ปัญหาอื่น ๆ ที่มีการแปลงเป็น JSON คือลำดับของคุณสมบัติในสตริงจะมีความสำคัญ {x:1, y:2}! =={y:2, x:1}
Stijn de Witt

81

ถ้าคุณกำลังทำงานในAngularJSที่angular.equalsฟังก์ชั่นจะตรวจสอบว่าวัตถุทั้งสองมีค่าเท่ากัน ในEmber.jsisEqualใช้

  • angular.equals- ดูเอกสารหรือแหล่งข้อมูลเพิ่มเติมเกี่ยวกับวิธีการนี้ มันเปรียบเทียบลึกลงไปในอาร์เรย์ด้วย
  • Ember.js isEqual- ดูเอกสารหรือแหล่งข้อมูลเพิ่มเติมเกี่ยวกับวิธีการนี้ มันไม่ได้ทำการเปรียบเทียบอย่างลึกซึ้งกับอาร์เรย์

var purple = [{"purple": "drank"}];
var drank = [{"purple": "drank"}];

if(angular.equals(purple, drank)) {
    document.write('got dat');
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js"></script>


66

นี่คือรุ่นของฉัน มันใช้คุณสมบัติObject.keysใหม่ที่นำมาใช้ใน ES5 และความคิด / การทดสอบจาก+ , +และ+ :

function objectEquals(x, y) {
    'use strict';

    if (x === null || x === undefined || y === null || y === undefined) { return x === y; }
    // after this just checking type of one would be enough
    if (x.constructor !== y.constructor) { return false; }
    // if they are functions, they should exactly refer to same one (because of closures)
    if (x instanceof Function) { return x === y; }
    // if they are regexps, they should exactly refer to same one (it is hard to better equality check on current ES)
    if (x instanceof RegExp) { return x === y; }
    if (x === y || x.valueOf() === y.valueOf()) { return true; }
    if (Array.isArray(x) && x.length !== y.length) { return false; }

    // if they are dates, they must had equal valueOf
    if (x instanceof Date) { return false; }

    // if they are strictly equal, they both need to be object at least
    if (!(x instanceof Object)) { return false; }
    if (!(y instanceof Object)) { return false; }

    // recursive object equality check
    var p = Object.keys(x);
    return Object.keys(y).every(function (i) { return p.indexOf(i) !== -1; }) &&
        p.every(function (i) { return objectEquals(x[i], y[i]); });
}


///////////////////////////////////////////////////////////////
/// The borrowed tests, run them by clicking "Run code snippet"
///////////////////////////////////////////////////////////////
var printResult = function (x) {
    if (x) { document.write('<div style="color: green;">Passed</div>'); }
    else { document.write('<div style="color: red;">Failed</div>'); }
};
var assert = { isTrue: function (x) { printResult(x); }, isFalse: function (x) { printResult(!x); } }
assert.isTrue(objectEquals(null,null));
assert.isFalse(objectEquals(null,undefined));
assert.isFalse(objectEquals(/abc/, /abc/));
assert.isFalse(objectEquals(/abc/, /123/));
var r = /abc/;
assert.isTrue(objectEquals(r, r));

assert.isTrue(objectEquals("hi","hi"));
assert.isTrue(objectEquals(5,5));
assert.isFalse(objectEquals(5,10));

assert.isTrue(objectEquals([],[]));
assert.isTrue(objectEquals([1,2],[1,2]));
assert.isFalse(objectEquals([1,2],[2,1]));
assert.isFalse(objectEquals([1,2],[1,2,3]));

assert.isTrue(objectEquals({},{}));
assert.isTrue(objectEquals({a:1,b:2},{a:1,b:2}));
assert.isTrue(objectEquals({a:1,b:2},{b:2,a:1}));
assert.isFalse(objectEquals({a:1,b:2},{a:1,b:3}));

assert.isTrue(objectEquals({1:{name:"mhc",age:28}, 2:{name:"arb",age:26}},{1:{name:"mhc",age:28}, 2:{name:"arb",age:26}}));
assert.isFalse(objectEquals({1:{name:"mhc",age:28}, 2:{name:"arb",age:26}},{1:{name:"mhc",age:28}, 2:{name:"arb",age:27}}));

Object.prototype.equals = function (obj) { return objectEquals(this, obj); };
var assertFalse = assert.isFalse,
    assertTrue = assert.isTrue;

assertFalse({}.equals(null));
assertFalse({}.equals(undefined));

assertTrue("hi".equals("hi"));
assertTrue(new Number(5).equals(5));
assertFalse(new Number(5).equals(10));
assertFalse(new Number(1).equals("1"));

assertTrue([].equals([]));
assertTrue([1,2].equals([1,2]));
assertFalse([1,2].equals([2,1]));
assertFalse([1,2].equals([1,2,3]));
assertTrue(new Date("2011-03-31").equals(new Date("2011-03-31")));
assertFalse(new Date("2011-03-31").equals(new Date("1970-01-01")));

assertTrue({}.equals({}));
assertTrue({a:1,b:2}.equals({a:1,b:2}));
assertTrue({a:1,b:2}.equals({b:2,a:1}));
assertFalse({a:1,b:2}.equals({a:1,b:3}));

assertTrue({1:{name:"mhc",age:28}, 2:{name:"arb",age:26}}.equals({1:{name:"mhc",age:28}, 2:{name:"arb",age:26}}));
assertFalse({1:{name:"mhc",age:28}, 2:{name:"arb",age:26}}.equals({1:{name:"mhc",age:28}, 2:{name:"arb",age:27}}));

var a = {a: 'text', b:[0,1]};
var b = {a: 'text', b:[0,1]};
var c = {a: 'text', b: 0};
var d = {a: 'text', b: false};
var e = {a: 'text', b:[1,0]};
var i = {
    a: 'text',
    c: {
        b: [1, 0]
    }
};
var j = {
    a: 'text',
    c: {
        b: [1, 0]
    }
};
var k = {a: 'text', b: null};
var l = {a: 'text', b: undefined};

assertTrue(a.equals(b));
assertFalse(a.equals(c));
assertFalse(c.equals(d));
assertFalse(a.equals(e));
assertTrue(i.equals(j));
assertFalse(d.equals(k));
assertFalse(k.equals(l));

// from comments on stackoverflow post
assert.isFalse(objectEquals([1, 2, undefined], [1, 2]));
assert.isFalse(objectEquals([1, 2, 3], { 0: 1, 1: 2, 2: 3 }));
assert.isFalse(objectEquals(new Date(1234), 1234));

// no two different function is equal really, they capture their context variables
// so even if they have same toString(), they won't have same functionality
var func = function (x) { return true; };
var func2 = function (x) { return true; };
assert.isTrue(objectEquals(func, func));
assert.isFalse(objectEquals(func, func2));
assert.isTrue(objectEquals({ a: { b: func } }, { a: { b: func } }));
assert.isFalse(objectEquals({ a: { b: func } }, { a: { b: func2 } }));


objectEquals([1,2,undefined],[1,2])ผลตอบแทนtrue
Roy Tinker

objectEquals([1,2,3],{0:1,1:2,2:3})ส่งกลับtrueเช่น - ไม่มีการตรวจสอบประเภทการตรวจสอบคีย์ / ค่าเท่านั้น
Roy Tinker

objectEquals(new Date(1234),1234)ผลตอบแทนtrue
Roy Tinker

1
if (x.constructor! == y.constructor) {return false; } สิ่งนี้จะผิดเพี้ยนเมื่อเปรียบเทียบ 'สตริงใหม่ (' a ')' สองรายการในหน้าต่างอื่น สำหรับความเท่าเทียมกันของคุณค่าคุณต้องตรวจสอบว่า String.isString บนวัตถุทั้งสองหรือไม่จากนั้นใช้การตรวจสอบความเท่าเทียมกันแบบหลวม 'a == b'
Triynko

1
มีความแตกต่างอย่างมากระหว่างความเท่าเทียมกันของ "คุณค่า" และความเท่าเทียม "เข้มงวด" และพวกเขาไม่ควรนำมาใช้ในทางเดียวกัน ความเท่าเทียมกันของค่าไม่ควรคำนึงถึงประเภทนอกเหนือจากโครงสร้างพื้นฐานซึ่งเป็นหนึ่งใน 4 เหล่านี้: 'วัตถุ' (เช่นชุดของคู่คีย์ / ค่า), 'หมายเลข', 'สตริง' หรือ 'อาร์เรย์' แค่นั้นแหละ. สิ่งใดที่ไม่ใช่ตัวเลขสตริงหรืออาร์เรย์ควรนำมาเปรียบเทียบเป็นชุดของคู่คีย์ / ค่าโดยไม่คำนึงถึงสิ่งที่เป็นตัวสร้าง (cross-window-safe) เมื่อเปรียบเทียบออบเจ็กต์ให้ถือเอาค่าของตัวเลขตามตัวอักษรและอินสแตนซ์ของ Number แต่ไม่บังคับให้ใช้สตริงกับตัวเลข
Triynko

50

หากคุณกำลังใช้ไลบรารี JSON คุณสามารถเข้ารหัสแต่ละวัตถุเป็น JSON จากนั้นเปรียบเทียบสตริงผลลัพธ์เพื่อความเท่าเทียมกัน

var obj1={test:"value"};
var obj2={test:"value2"};

alert(JSON.encode(obj1)===JSON.encode(obj2));

หมายเหตุ: แม้ว่าคำตอบนี้จะใช้งานได้ในหลายกรณี แต่หลายคนได้ชี้ให้เห็นในความคิดเห็นว่ามันเป็นปัญหาด้วยเหตุผลหลายประการ ในทุกกรณีคุณจะต้องหาโซลูชันที่มีประสิทธิภาพมากขึ้น


91
ที่น่าสนใจ แต่เป็นเรื่องยุ่งยากเล็กน้อยในความคิดของฉัน ตัวอย่างเช่นคุณสามารถรับประกันได้ 100% ว่าคุณสมบัติของวัตถุจะถูกสร้างขึ้นในลำดับเดียวกันเสมอหรือไม่
Guido

25
นั่นเป็นคำถามที่ดีและยกวัตถุอื่นขึ้นมาว่าวัตถุสองชิ้นที่มีคุณสมบัติเหมือนกันในคำสั่งซื้อที่ต่างกันมีความเท่ากันหรือไม่ ขึ้นอยู่กับสิ่งที่คุณหมายถึงเท่ากับฉันเดา
Joel Anair

11
โปรดทราบว่าตัวเข้ารหัสและตัวแยกสตริงส่วนใหญ่จะไม่สนใจฟังก์ชันและแปลงตัวเลขที่ไม่ จำกัด เช่น NaN เป็นโมฆะ
Stephen Belanger

4
ฉันเห็นด้วยกับ Guido ลำดับของคุณสมบัติมีความสำคัญและไม่สามารถรับประกันได้ @ JoelAnair ฉันคิดว่าสองวัตถุที่มีคุณสมบัติเดียวกันในคำสั่งต่าง ๆ ควรได้รับการพิจารณาที่เท่าเทียมกันหากมูลค่าของคุณสมบัติเท่ากัน
Juzer Ali

5
สิ่งนี้สามารถทำงานกับ stringifier JSON ทางเลือกอันหนึ่งที่เรียงลำดับคีย์อ็อบเจ็กต์อย่างสม่ำเสมอ
Roy Tinker

40

deepEqualการใช้งานสั้น:

function deepEqual(x, y) {
  return (x && y && typeof x === 'object' && typeof y === 'object') ?
    (Object.keys(x).length === Object.keys(y).length) &&
      Object.keys(x).reduce(function(isEqual, key) {
        return isEqual && deepEqual(x[key], y[key]);
      }, true) : (x === y);
}

แก้ไข : เวอร์ชัน 2 โดยใช้คำแนะนำของ jib และฟังก์ชั่นลูกศร ES6:

function deepEqual(x, y) {
  const ok = Object.keys, tx = typeof x, ty = typeof y;
  return x && y && tx === 'object' && tx === ty ? (
    ok(x).length === ok(y).length &&
      ok(x).every(key => deepEqual(x[key], y[key]))
  ) : (x === y);
}

5
คุณสามารถแทนที่reduceด้วยeveryเพื่อลดความซับซ้อน
jib

1
@nonkertompf แน่ใจว่าเขาทำได้: Object.keys(x).every(key => deepEqual(x[key], y[key])).
jib

2
สิ่งนี้จะล้มเหลวเมื่อคุณทำการเปรียบเทียบสองวัน
เกร็ก

3
deepEqual ({}, []) ผลตอบแทนจริง
AlexMorley-Finch

3
ใช่ถ้าคุณสนใจกรณีมุมเช่นนั้นวิธีแก้ปัญหาที่น่าเกลียดคือแทนที่: (x === y)ด้วย: (x === y && (x != null && y != null || x.constructor === y.constructor))
atmin

22

คุณกำลังพยายามทดสอบว่าวัตถุสองอย่างเท่ากันหรือไม่? เช่น: คุณสมบัติของพวกเขาเท่ากัน?

หากเป็นกรณีนี้คุณอาจสังเกตเห็นสถานการณ์นี้:

var a = { foo : "bar" };
var b = { foo : "bar" };
alert (a == b ? "Equal" : "Not equal");
// "Not equal"

คุณอาจต้องทำสิ่งนี้:

function objectEquals(obj1, obj2) {
    for (var i in obj1) {
        if (obj1.hasOwnProperty(i)) {
            if (!obj2.hasOwnProperty(i)) return false;
            if (obj1[i] != obj2[i]) return false;
        }
    }
    for (var i in obj2) {
        if (obj2.hasOwnProperty(i)) {
            if (!obj1.hasOwnProperty(i)) return false;
            if (obj1[i] != obj2[i]) return false;
        }
    }
    return true;
}

เห็นได้ชัดว่าฟังก์ชั่นสามารถทำได้ด้วยการปรับให้เหมาะสมเล็กน้อยและความสามารถในการตรวจสอบอย่างละเอียด (เพื่อจัดการกับวัตถุที่ซ้อนกัน:) var a = { foo : { fu : "bar" } }แต่คุณได้รับแนวคิด

ตามที่ได้กล่าวไว้คุณอาจต้องปรับเปลี่ยนสิ่งนี้เพื่อจุดประสงค์ของคุณเองเช่น: คลาสที่แตกต่างกันอาจมีคำจำกัดความที่แตกต่างกันของ "เท่ากัน" หากคุณกำลังทำงานกับวัตถุธรรมดาดังกล่าวข้างต้นอาจพอเพียงมิฉะนั้นMyClass.equals()ฟังก์ชั่นที่กำหนดเองอาจเป็นวิธีที่จะไป


มันเป็นวิธีการที่ยาวนาน แต่มันทำการทดสอบวัตถุอย่างสมบูรณ์โดยไม่ต้องทำการตั้งสมมติฐานใด ๆ กับลำดับของคุณสมบัติในแต่ละวัตถุ
briancollins081

22

หากคุณมีฟังก์ชั่นการทำสำเนาลึกคุณสามารถใช้เคล็ดลับต่อไปนี้เพื่อยังคงใช้งานได้JSON.stringifyในขณะที่จับคู่ลำดับคุณสมบัติ:

function equals(obj1, obj2) {
    function _equals(obj1, obj2) {
        return JSON.stringify(obj1)
            === JSON.stringify($.extend(true, {}, obj1, obj2));
    }
    return _equals(obj1, obj2) && _equals(obj2, obj1);
}

ตัวอย่าง: http://jsfiddle.net/CU3vb/3/

เหตุผล:

เนื่องจากคุณสมบัติของobj1จะถูกคัดลอกไปยังโคลนหนึ่งโดยหนึ่งคำสั่งของพวกเขาในโคลนจะถูกเก็บไว้ และเมื่อคุณสมบัติของobj2จะถูกคัดลอกไปยังโคลนเนื่องจากคุณสมบัติที่มีอยู่แล้วในobj1จะถูกเขียนทับคำสั่งของพวกเขาในโคลนจะถูกเก็บไว้


11
ฉันไม่คิดว่ารับประกันการสั่งซื้อในเบราว์เซอร์ / เครื่องมือ
Jo Liss

@JoLiss ต้องการการอ้างอิง;) ฉันจำการทดสอบนี้ในหลายเบราว์เซอร์เพื่อรับผลลัพธ์ที่สอดคล้องกัน แต่แน่นอนไม่มีใครสามารถรับประกันพฤติกรรมที่ยังคงเหมือนเดิมในเบราว์เซอร์ / เครื่องมือในอนาคต นี่เป็นกลอุบาย (ดังที่ได้กล่าวแล้วในคำตอบ) ที่ดีที่สุดและฉันไม่ได้หมายความว่ามันจะเป็นวิธีที่แน่นอนในการเปรียบเทียบวัตถุ
Ates Goral

1
แน่นอนนี่คือตัวชี้: ECMAScript spec บอกว่า object คือ "unordered" ; และคำตอบนี้สำหรับพฤติกรรมการเบี่ยงเบนที่แท้จริงในเบราว์เซอร์ปัจจุบัน
Jo Liss

2
@ JoLiss ขอบคุณสำหรับสิ่งนั้น! แต่โปรดทราบว่าฉันไม่เคยอ้างสิทธิ์การเก็บรักษาคำสั่งระหว่างรหัสและวัตถุที่รวบรวม ฉันอ้างสิทธิ์ในการเก็บรักษาลำดับของคุณสมบัติที่มีค่าถูกแทนที่ในสถานที่ นั่นคือกุญแจสำคัญในโซลูชันของฉัน: การใช้มิกซ์อินเพื่อเขียนทับค่าคุณสมบัติ สมมติว่าการใช้งานโดยทั่วไปเลือกที่จะใช้ hashmap บางประเภทการแทนที่เพียงค่าควรรักษาลำดับของคีย์ อันที่จริงแล้วฉันได้ทำการทดสอบในเบราว์เซอร์ที่แตกต่างกัน
Ates Goral

1
@AtesGoral: เป็นไปได้ไหมที่จะทำให้ข้อ จำกัด นี้ชัดเจนยิ่งขึ้น(ตัวหนา, ... ) คนส่วนใหญ่ก็ทำcopy-pasteโดยไม่ต้องอ่านข้อความที่อยู่รอบ ๆ มัน ...
วิลเล็มแวน Onsem

21

ใน Node.js require("assert").deepStrictEqualคุณสามารถใช้พื้นเมือง ข้อมูลเพิ่มเติม: http://nodejs.org/api/assert.html

ตัวอย่างเช่น:

var assert = require("assert");
assert.deepStrictEqual({a:1, b:2}, {a:1, b:3}); // will throw AssertionError

อีกตัวอย่างหนึ่งที่ส่งคืนtrue/ falseแทนที่จะส่งคืนข้อผิดพลาด:

var assert = require("assert");

function deepEqual(a, b) {
    try {
      assert.deepEqual(a, b);
    } catch (error) {
      if (error.name === "AssertionError") {
        return false;
      }
      throw error;
    }
    return true;
};

Chaiมีคุณสมบัตินี้ด้วย ในกรณีนี้คุณจะใช้:var foo = { a: 1 }; var bar = { a: 1 }; expect(foo).to.deep.equal(bar); // true;
Folusho Oladipo

บางรุ่น Node.js ชุดไปerror.name ในกรณีนี้ผมเปลี่ยนคำสั่งถ้ามี"AssertionError [ERR_ASSERTION]" if (error.code === 'ERR_ASSERTION') {
Knute Knudsen

ฉันไม่รู้deepStrictEqualว่าจะไปทางไหน ฉันพินาศสมองของฉันพยายามคิดออกว่าทำไมstrictEqualไม่ทำงาน น่าอัศจรรย์
NetOperator Wibby

19

โซลูชันที่ง่ายและตรรกะสำหรับการเปรียบเทียบทุกอย่างเช่นObject, Array, String, Int ...

JSON.stringify({a: val1}) === JSON.stringify({a: val2})

บันทึก:

  • คุณต้องแทนที่val1และval2ด้วยวัตถุของคุณ
  • สำหรับวัตถุคุณต้องเรียงลำดับ (ตามคีย์) ซ้ำสำหรับวัตถุทั้งสองด้าน

6
ฉันสมมติว่าสิ่งนี้จะไม่ทำงานในหลาย ๆ กรณีเพราะลำดับของคีย์ในวัตถุไม่สำคัญ - เว้นแต่JSON.stringifyจะเรียงลำดับตัวอักษรใหม่ (ซึ่งฉันไม่พบเอกสาร )
Bram Vanroy

yup คุณพูดถูก ... สำหรับวัตถุคุณต้องเรียงลำดับแบบวนซ้ำสำหรับวัตถุทั้งสองด้าน
Pratik Bhalodiya

2
สิ่งนี้ไม่ทำงานสำหรับวัตถุที่มีการอ้างอิงแบบวงกลม
Nate-Bit Int

13

ฉันใช้comparableฟังก์ชันนี้เพื่อสร้างสำเนาของวัตถุที่เทียบเท่ากับ JSON:

var comparable = o => (typeof o != 'object' || !o)? o :
  Object.keys(o).sort().reduce((c, key) => (c[key] = comparable(o[key]), c), {});

// Demo:

var a = { a: 1, c: 4, b: [2, 3], d: { e: '5', f: null } };
var b = { b: [2, 3], c: 4, d: { f: null, e: '5' }, a: 1 };

console.log(JSON.stringify(comparable(a)));
console.log(JSON.stringify(comparable(b)));
console.log(JSON.stringify(comparable(a)) == JSON.stringify(comparable(b)));
<div id="div"></div>

มีประโยชน์ในการทดสอบ (กรอบการทดสอบส่วนใหญ่มีisฟังก์ชั่น) เช่น

is(JSON.stringify(comparable(x)), JSON.stringify(comparable(y)), 'x must match y');

หากจับความแตกต่างสตริงจะถูกบันทึกและสร้างความแตกต่างได้:

x must match y
got      {"a":1,"b":{"0":2,"1":3},"c":7,"d":{"e":"5","f":null}},
expected {"a":1,"b":{"0":2,"1":3},"c":4,"d":{"e":"5","f":null}}.

1
ความคิดที่ดี (ในกรณีของฉันวัตถุที่จะนำมาเปรียบเทียบเพียงคีย์ / ค่าคู่ไม่มีสิ่งพิเศษ)
เมค

10

นี่เป็นวิธีแก้ปัญหาใน ES6 / ES2015 โดยใช้วิธีการทำงานแบบ:

const typeOf = x => 
  ({}).toString
      .call(x)
      .match(/\[object (\w+)\]/)[1]

function areSimilar(a, b) {
  const everyKey = f => Object.keys(a).every(f)

  switch(typeOf(a)) {
    case 'Array':
      return a.length === b.length &&
        everyKey(k => areSimilar(a.sort()[k], b.sort()[k]));
    case 'Object':
      return Object.keys(a).length === Object.keys(b).length &&
        everyKey(k => areSimilar(a[k], b[k]));
    default:
      return a === b;
  }
}

ตัวอย่างที่นี่


ไม่ทำงานหากลำดับของคีย์วัตถุมีการเปลี่ยนแปลง
Isaac Pak

9

ฉันไม่ทราบว่ามีใครโพสต์สิ่งที่คล้ายกับนี้หรือไม่ แต่นี่คือฟังก์ชันที่ฉันทำเพื่อตรวจสอบความเท่าเทียมกันของวัตถุ

function objectsAreEqual(a, b) {
  for (var prop in a) {
    if (a.hasOwnProperty(prop)) {
      if (b.hasOwnProperty(prop)) {
        if (typeof a[prop] === 'object') {
          if (!objectsAreEqual(a[prop], b[prop])) return false;
        } else {
          if (a[prop] !== b[prop]) return false;
        }
      } else {
        return false;
      }
    }
  }
  return true;
}

นอกจากนี้มันยังเรียกซ้ำได้ดังนั้นจึงสามารถตรวจสอบความเท่าเทียมกันอย่างลึกซึ้งหากนั่นคือสิ่งที่คุณเรียกว่า


การแก้ไขเล็กน้อย: ก่อนที่จะผ่านแต่ละอุปกรณ์ประกอบฉากใน a และ b เพิ่มการตรวจสอบนี้ถ้า (Object.getOwnPropertyNames (a) .length! == Object.getOwnPropertyNames (b) .length) ส่งคืนค่าเท็จ
Hith

1
เป็นที่ชัดเจนว่าตัวตรวจสอบความเท่าเทียมกันที่เหมาะสมจะต้องเรียกซ้ำ ฉันคิดว่าหนึ่งในคำตอบแบบเรียกซ้ำควรเป็นคำตอบที่ถูกต้อง คำตอบที่ได้รับการยอมรับไม่ได้ให้รหัสและมันไม่ได้ช่วย
canbax

9

สำหรับผู้ที่คุณใช้ NodeJS มีวิธีที่สะดวกในการเรียกใช้isDeepStrictEqualบนไลบรารี Util ดั้งเดิมที่สามารถทำได้

const util = require('util');

const obj1 = {
  foo: "bar",
  baz: [1, 2]
};

const obj2 = {
  foo: "bar",
  baz: [1, 2]
};


obj1 == obj2 // false
util.isDeepStrictEqual(obj1, obj2) // true

https://nodejs.org/api/util.html#util_util_isdeepstrictequal_val1_val2


ประสิทธิภาพของมันน่าจะดี ไม่ต้องห่วง. แม้ฉันจะใช้มันในสถานการณ์ที่ซับซ้อนเช่นกัน เมื่อเราใช้สิ่งนี้เราไม่ต้องกังวลว่าทรัพย์สินของวัตถุจะแบกวัตถุหรืออาร์เรย์ Json.Stringify ทำให้เป็นสตริงอยู่ดีและการเปรียบเทียบสตริงใน javascript ไม่ใช่เรื่องใหญ่
TrickOrTreat

6

ES6:รหัสขั้นต่ำที่ฉันสามารถทำได้ก็คือ ทำการเปรียบเทียบแบบวนซ้ำโดยการทำให้สตริงวัตถุทั้งหมดมีข้อ จำกัด เพียงอย่างเดียวคือไม่มีการเปรียบเทียบวิธีการหรือสัญลักษณ์

const compareObjects = (a, b) => { 
  let s = (o) => Object.entries(o).sort().map(i => { 
     if(i[1] instanceof Object) i[1] = s(i[1]);
     return i 
  }) 
  return JSON.stringify(s(a)) === JSON.stringify(s(b))
}

console.log(compareObjects({b:4,a:{b:1}}, {a:{b:1},b:4}));


นี่เป็นคำตอบที่ใช้งานได้ดีขอบคุณ @Adriano Spadoni คุณรู้หรือไม่ว่าฉันจะได้รับคีย์ / คุณลักษณะที่ได้รับการแก้ไขได้อย่างไร ขอขอบคุณ
digitai

1
สวัสดี @digital หากคุณต้องการคีย์ที่แตกต่างกันนี่ไม่ใช่ฟังก์ชั่นที่เหมาะ ตรวจสอบคำตอบอื่น ๆ และใช้คำตอบวนรอบวัตถุ
Adriano Spadoni

5

คุณสามารถใช้_.isEqual(obj1, obj2)จากไลบรารี underscore.js

นี่คือตัวอย่าง:

var stooge = {name: 'moe', luckyNumbers: [13, 27, 34]};
var clone  = {name: 'moe', luckyNumbers: [13, 27, 34]};
stooge == clone;
=> false
_.isEqual(stooge, clone);
=> true

ดูเอกสารอย่างเป็นทางการจากที่นี่: http://underscorejs.org/#isEqual


5

สมมติว่าลำดับของคุณสมบัติในวัตถุไม่เปลี่ยนแปลง

JSON.stringify ()ทำงานกับวัตถุทั้งสองชนิดที่มีความลึกและไม่ลึกไม่แน่ใจในแง่ของประสิทธิภาพ:

var object1 = {
  key: "value"
};

var object2 = {
  key: "value"
};

var object3 = {
  key: "no value"
};

console.log('object1 and object2 are equal: ', JSON.stringify(object1) === JSON.stringify(object2));

console.log('object2 and object3 are equal: ', JSON.stringify(object2) === JSON.stringify(object3));


1
สิ่งนี้ไม่ได้ทำในสิ่งที่ OP ต้องการเนื่องจากมันจะจับคู่ก็ต่อเมื่อวัตถุทั้งคู่มีคีย์เหมือนกันทั้งหมดซึ่งพวกมันระบุว่าจะไม่ทำเช่นนั้น นอกจากนี้ยังจะต้องใช้คีย์ในลำดับเดียวกันซึ่งยังไม่สมเหตุสมผลจริงๆ
SpeedOfRound

1
คุณสมบัติคืออะไรในลำดับที่แตกต่างกัน ??? ไม่ใช่วิธีที่ดี
Vishal Sakaria

4

ทางออกที่ง่ายสำหรับปัญหานี้ที่หลายคนไม่ทราบคือการจัดเรียงสตริง JSON (ต่อตัวอักษร) ซึ่งมักจะเร็วกว่าโซลูชันอื่น ๆ ที่กล่าวถึงที่นี่:

function areEqual(obj1, obj2) {
    var a = JSON.stringify(obj1), b = JSON.stringify(obj2);
    if (!a) a = '';
    if (!b) b = '';
    return (a.split('').sort().join('') == b.split('').sort().join(''));
}

อีกสิ่งที่มีประโยชน์เกี่ยวกับวิธีนี้คือคุณสามารถกรองการเปรียบเทียบโดยส่งผ่านฟังก์ชัน "replacer" ไปยังฟังก์ชัน JSON.stringify ( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON / stringify # Example_of_using_replacer_parameter ) ต่อไปนี้จะเปรียบเทียบคีย์วัตถุทั้งหมดที่ชื่อ "derp" เท่านั้น:

function areEqual(obj1, obj2, filter) {
    var a = JSON.stringify(obj1, filter), b = JSON.stringify(obj2, filter);
    if (!a) a = '';
    if (!b) b = '';
    return (a.split('').sort().join('') == b.split('').sort().join(''));
}
var equal = areEqual(obj1, obj2, function(key, value) {
    return (key === 'derp') ? value : undefined;
});

1
โอ้ฉันลืมไปแล้ว แต่ฟังก์ชั่นสามารถเร่งความเร็วได้โดยการทดสอบวัตถุแรกที่เท่ากับและประกันตัวเร็วถ้ามันเป็นวัตถุเดียวกัน: ถ้า (obj1 === obj2) คืนค่าจริง
th317erd

11
areEqual({a: 'b'}, {b: 'a'})ได้รับtrueแล้ว
okm

ใช่ฉันรู้หลังจากโพสต์ว่า "การแก้ปัญหา" นี้มีปัญหา ต้องใช้งานอีกเล็กน้อยในอัลกอริทึมการเรียงลำดับเพื่อให้ทำงานได้อย่างถูกต้อง
th317erd

4

แค่อยากมีส่วนร่วมในการเปรียบเทียบวัตถุในรุ่นของฉันโดยใช้คุณสมบัติบางอย่างของ es6 ไม่คำนึงถึงการสั่งซื้อ หลังจากการแปลงทั้งหมดหาก / อื่น ๆ เป็นแบบไตรภาคฉันได้รับสิ่งต่อไปนี้:

function areEqual(obj1, obj2) {

    return Object.keys(obj1).every(key => {

            return obj2.hasOwnProperty(key) ?
                typeof obj1[key] === 'object' ?
                    areEqual(obj1[key], obj2[key]) :
                obj1[key] === obj2[key] :
                false;

        }
    )
}

3

ฉันต้องการฟังก์ชั่นเปรียบเทียบวัตถุทั่วไปมากกว่าที่โพสต์ไว้ฉันจึงทำสิ่งต่อไปนี้ คำติชมชื่นชม ...

Object.prototype.equals = function(iObj) {
  if (this.constructor !== iObj.constructor)
    return false;
  var aMemberCount = 0;
  for (var a in this) {
    if (!this.hasOwnProperty(a))
      continue;
    if (typeof this[a] === 'object' && typeof iObj[a] === 'object' ? !this[a].equals(iObj[a]) : this[a] !== iObj[a])
      return false;
    ++aMemberCount;
  }
  for (var a in iObj)
    if (iObj.hasOwnProperty(a))
      --aMemberCount;
  return aMemberCount ? false : true;
}

2
ฉันลงเอยด้วยรูปแบบของสิ่งนี้ ขอบคุณสำหรับแนวคิดของการนับสมาชิก!
NateS

2
ใช้ความระมัดระวังอย่างมากเกี่ยวกับการปรับเปลี่ยนObject.prototype- ในกรณีส่วนใหญ่ไม่แนะนำให้ทำ (ตัวอย่างเพิ่มเติมปรากฏในทุก .. อาจจะพิจารณาObject.equals = function(aObj, bObj) {...}?
Roy Tinker

3

หากคุณกำลังเปรียบเทียบวัตถุ JSON คุณสามารถใช้https://github.com/mirek/node-rus-diff

npm install rus-diff

การใช้งาน:

a = {foo:{bar:1}}
b = {foo:{bar:1}}
c = {foo:{bar:2}}

var rusDiff = require('rus-diff').rusDiff

console.log(rusDiff(a, b)) // -> false, meaning a and b are equal
console.log(rusDiff(a, c)) // -> { '$set': { 'foo.bar': 2 } }

หากวัตถุสองชิ้นแตกต่างกัน MongoDB ที่เข้ากันได้{$rename:{...}, $unset:{...}, $set:{...}}จะกลับมาเป็นเหมือนวัตถุ


3

ฉันประสบปัญหาเดียวกันและตัดสินใจที่จะเขียนวิธีแก้ปัญหาของตัวเอง แต่เนื่องจากฉันต้องการเปรียบเทียบอาร์เรย์กับวัตถุและในทางกลับกันฉันจึงสร้างโซลูชันทั่วไป ฉันตัดสินใจที่จะเพิ่มฟังก์ชั่นลงในต้นแบบ แต่มีหนึ่งสามารถเขียนลงในฟังก์ชันสแตนด์อโลนได้อย่างง่ายดาย นี่คือรหัส:

Array.prototype.equals = Object.prototype.equals = function(b) {
    var ar = JSON.parse(JSON.stringify(b));
    var err = false;
    for(var key in this) {
        if(this.hasOwnProperty(key)) {
            var found = ar.find(this[key]);
            if(found > -1) {
                if(Object.prototype.toString.call(ar) === "[object Object]") {
                    delete ar[Object.keys(ar)[found]];
                }
                else {
                    ar.splice(found, 1);
                }
            }
            else {
                err = true;
                break;
            }
        }
    };
    if(Object.keys(ar).length > 0 || err) {
        return false;
    }
    return true;
}

Array.prototype.find = Object.prototype.find = function(v) {
    var f = -1;
    for(var i in this) {
        if(this.hasOwnProperty(i)) {
            if(Object.prototype.toString.call(this[i]) === "[object Array]" || Object.prototype.toString.call(this[i]) === "[object Object]") {
                if(this[i].equals(v)) {
                    f = (typeof(i) == "number") ? i : Object.keys(this).indexOf(i);
                }
            }
            else if(this[i] === v) {
                f = (typeof(i) == "number") ? i : Object.keys(this).indexOf(i);
            }
        }
    }
    return f;
}

อัลกอริทึมนี้แบ่งออกเป็นสองส่วน ฟังก์ชันมีค่าเท่ากับฟังก์ชันและฟังก์ชันเพื่อค้นหาดัชนีตัวเลขของคุณสมบัติในอาร์เรย์ / วัตถุ ฟังก์ชั่น find จำเป็นเพียงเพราะ indexof ค้นหาตัวเลขและสตริงเท่านั้นและไม่มีวัตถุใด ๆ

เราสามารถเรียกมันได้ว่า:

({a: 1, b: "h"}).equals({a: 1, b: "h"});

ฟังก์ชันจะคืนค่าจริงหรือเท็จในกรณีนี้จริง อัลกอริทึม als ช่วยให้การเปรียบเทียบระหว่างวัตถุที่ซับซ้อนมาก:

({a: 1, b: "hello", c: ["w", "o", "r", "l", "d", {answer1: "should be", answer2: true}]}).equals({b: "hello", a: 1, c: ["w", "d", "o", "r", {answer1: "should be", answer2: true}, "l"]})

ตัวอย่างด้านบนจะกลับมาเป็นจริงแม้คุณสมบัติจะมีการสั่งซื้อที่แตกต่างกัน รายละเอียดเล็ก ๆ น้อย ๆ ที่ควรระวัง: รหัสนี้จะตรวจสอบตัวแปรสองชนิดเดียวกันดังนั้น "3" จึงไม่เหมือนกับ 3


3

ฉันเห็นคำตอบรหัสสปาเก็ตตี้ โดยไม่ต้องใช้ libs ของบุคคลที่สามใด ๆ นี่เป็นเรื่องง่ายมาก

ขั้นแรกให้เรียงลำดับวัตถุสองรายการด้วยคีย์ชื่อคีย์ของพวกเขา

let objectOne = { hey, you }
let objectTwo = { you, hey }

// If you really wanted you could make this recursive for deep sort.
const sortObjectByKeyname = (objectToSort) => {
    return Object.keys(objectToSort).sort().reduce((r, k) => (r[k] = objectToSort[k], r), {});
}

let objectOne = sortObjectByKeyname(objectOne)
let objectTwo = sortObjectByKeyname(objectTwo)

จากนั้นใช้สตริงเพื่อเปรียบเทียบ

JSON.stringify(objectOne) === JSON.stringify(objectTwo)

สิ่งนี้ยังใช้ไม่ได้กับการทำสำเนาลึก แต่มีความลึกการวนซ้ำเพียงครั้งเดียว
andras

ฉันคิดว่า @andras หมายถึงคุณจำเป็นต้องเรียงลำดับคีย์ของวัตถุที่ซ้อนกันซ้ำ ๆ
Davi Lima

2

ฉันขอแนะนำให้ต่อต้านการแฮชหรือการทำให้เป็นอนุกรม (ตามที่แนะนำให้ใช้โซลูชัน JSON) หากคุณต้องการทดสอบว่าวัตถุสองอย่างเท่ากันคุณต้องกำหนดความหมายที่เท่ากับ อาจเป็นได้ว่าสมาชิกข้อมูลทั้งหมดในวัตถุทั้งคู่ตรงกันหรืออาจเป็นได้ว่าต้องตรงกับตำแหน่งหน่วยความจำ (หมายถึงตัวแปรทั้งสองอ้างอิงวัตถุเดียวกันในหน่วยความจำ) หรืออาจเป็นได้ว่าสมาชิกข้อมูลเพียงคนเดียวในแต่ละวัตถุต้องตรงกัน

เมื่อเร็ว ๆ นี้ฉันได้พัฒนาวัตถุที่ตัวสร้างสร้าง ID ใหม่ (เริ่มจาก 1 และเพิ่มขึ้น 1) ทุกครั้งที่มีการสร้างอินสแตนซ์ วัตถุนี้มีฟังก์ชั่น isEqual ที่เปรียบเทียบค่า id นั้นกับค่า id ของวัตถุอื่นและคืนค่าจริงถ้าจับคู่กัน

ในกรณีนั้นฉันกำหนด "เท่ากับ" ตามความหมายของค่า id ที่ตรงกัน เนื่องจากแต่ละอินสแตนซ์มี id ที่ไม่ซ้ำกันซึ่งสามารถใช้เพื่อบังคับใช้ความคิดที่ว่าวัตถุที่ตรงกันนั้นยังครอบครองตำแหน่งหน่วยความจำเดียวกัน แม้ว่าจะไม่จำเป็น


2

มันมีประโยชน์ที่จะต้องพิจารณาสองวัตถุที่เท่ากันหากพวกเขามีค่าเดียวกันทั้งหมดสำหรับคุณสมบัติทั้งหมดและเรียกซ้ำสำหรับวัตถุและอาร์เรย์ที่ซ้อนกันทั้งหมด ฉันยังพิจารณาสองวัตถุต่อไปนี้เท่ากัน:

var a = {p1: 1};
var b = {p1: 1, p2: undefined};

ในทำนองเดียวกันอาร์เรย์อาจมีองค์ประกอบ "ขาดหาย" และองค์ประกอบที่ไม่ได้กำหนด ฉันจะปฏิบัติต่อผู้ที่เหมือนกัน:

var c = [1, 2];
var d = [1, 2, undefined];

ฟังก์ชั่นที่ใช้นิยามความเท่าเทียมกันนี้:

function isEqual(a, b) {
    if (a === b) {
        return true;
    }

    if (generalType(a) != generalType(b)) {
        return false;
    }

    if (a == b) {
        return true;
    }

    if (typeof a != 'object') {
        return false;
    }

    // null != {}
    if (a instanceof Object != b instanceof Object) {
        return false;
    }

    if (a instanceof Date || b instanceof Date) {
        if (a instanceof Date != b instanceof Date ||
            a.getTime() != b.getTime()) {
            return false;
        }
    }

    var allKeys = [].concat(keys(a), keys(b));
    uniqueArray(allKeys);

    for (var i = 0; i < allKeys.length; i++) {
        var prop = allKeys[i];
        if (!isEqual(a[prop], b[prop])) {
            return false;
        }
    }
    return true;
}

รหัสที่มา (รวมถึงฟังก์ชั่นผู้ช่วย, generalType และ uniqueArray): หน่วยทดสอบและทดสอบวิ่งที่นี่


2

ฉันใช้สมมติฐานต่อไปนี้กับฟังก์ชันนี้:

  1. คุณควบคุมวัตถุที่คุณกำลังเปรียบเทียบและคุณมีค่าดั้งเดิมเท่านั้น (เช่นไม่ใช่วัตถุที่ซ้อนกันฟังก์ชั่น ฯลฯ )
  2. เบราว์เซอร์ของคุณมีการสนับสนุนObject.keys

สิ่งนี้ควรได้รับการปฏิบัติเสมือนเป็นการสาธิตกลยุทธ์ง่ายๆ

/**
 * Checks the equality of two objects that contain primitive values. (ie. no nested objects, functions, etc.)
 * @param {Object} object1
 * @param {Object} object2
 * @param {Boolean} [order_matters] Affects the return value of unordered objects. (ex. {a:1, b:2} and {b:2, a:1}).
 * @returns {Boolean}
 */
function isEqual( object1, object2, order_matters ) {
    var keys1 = Object.keys(object1),
        keys2 = Object.keys(object2),
        i, key;

    // Test 1: Same number of elements
    if( keys1.length != keys2.length ) {
        return false;
    }

    // If order doesn't matter isEqual({a:2, b:1}, {b:1, a:2}) should return true.
    // keys1 = Object.keys({a:2, b:1}) = ["a","b"];
    // keys2 = Object.keys({b:1, a:2}) = ["b","a"];
    // This is why we are sorting keys1 and keys2.
    if( !order_matters ) {
        keys1.sort();
        keys2.sort();
    }

    // Test 2: Same keys
    for( i = 0; i < keys1.length; i++ ) {
        if( keys1[i] != keys2[i] ) {
            return false;
        }
    }

    // Test 3: Values
    for( i = 0; i < keys1.length; i++ ) {
        key = keys1[i];
        if( object1[key] != object2[key] ) {
            return false;
        }
    }

    return true;
}

2

นี่คือส่วนเพิ่มเติมสำหรับทั้งหมดข้างต้นไม่ใช่การทดแทน หากคุณต้องการเปรียบเทียบวัตถุที่ตื้นอย่างรวดเร็วโดยไม่จำเป็นต้องตรวจสอบกรณีซ้ำซ้ำ นี่คือช็อตเด็ด

สิ่งนี้เปรียบเทียบสำหรับ: 1) ความเท่าเทียมกันของจำนวนคุณสมบัติของตัวเอง, 2) ความเท่าเทียมกันของชื่อคีย์, 3) ถ้า bCompareValues ​​== จริง, ความเท่าเทียมกันของค่าคุณสมบัติที่สอดคล้องกันและประเภทของพวกเขา (ความเท่าเทียมกันสาม)

var shallowCompareObjects = function(o1, o2, bCompareValues) {
    var s, 
        n1 = 0,
        n2 = 0,
        b  = true;

    for (s in o1) { n1 ++; }
    for (s in o2) { 
        if (!o1.hasOwnProperty(s)) {
            b = false;
            break;
        }
        if (bCompareValues && o1[s] !== o2[s]) {
            b = false;
            break;
        }
        n2 ++;
    }
    return b && n1 == n2;
}

2

สำหรับการเปรียบเทียบคีย์สำหรับอินสแตนซ์ออบเจ็กต์คู่ของคีย์ / ค่าอย่างง่ายฉันใช้:

function compareKeys(r1, r2) {
    var nloops = 0, score = 0;
    for(k1 in r1) {
        for(k2 in r2) {
            nloops++;
            if(k1 == k2)
                score++; 
        }
    }
    return nloops == (score * score);
};

เมื่อเปรียบเทียบกับคีย์แล้วการเพิ่มfor..inลูปธรรมดาก็เพียงพอแล้ว

ความซับซ้อนคือ O (N * N) โดยที่ N คือจำนวนของปุ่ม

ฉันหวัง / เดาวัตถุที่ฉันกำหนดจะไม่เก็บคุณสมบัติมากกว่า 1,000 รายการ ...


2

ฉันรู้ว่ามันค่อนข้างเก่า แต่ฉันต้องการเพิ่มวิธีแก้ไขปัญหาที่เกิดขึ้นสำหรับปัญหานี้ ฉันมีวัตถุและฉันต้องการที่จะรู้ว่าเมื่อมันมีการเปลี่ยนแปลงข้อมูล "สิ่งที่คล้ายกับ Object.observe" และสิ่งที่ฉันทำคือ:

function checkObjects(obj,obj2){
   var values = [];
   var keys = [];
   keys = Object.keys(obj);
   keys.forEach(function(key){
      values.push(key);
   });
   var values2 = [];
   var keys2 = [];
   keys2 = Object.keys(obj2);
   keys2.forEach(function(key){
      values2.push(key);
   });
   return (values == values2 && keys == keys2)
}

นี่สามารถทำซ้ำและสร้างชุดอาร์เรย์อื่นเพื่อเปรียบเทียบค่าและคีย์ มันง่ายมากเพราะตอนนี้พวกเขาอยู่ในอาร์เรย์และจะคืนค่าเท็จหากวัตถุมีขนาดแตกต่างกัน


1
นี้มักจะกลับมาเพราะอาร์เรย์ไม่ได้เปรียบเทียบโดยค่าเช่นfalse [1,2] != [1,2]
jib
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.