Có loại hàm băm nào trong JavaScript không?


150

Về cơ bản, tôi đang cố gắng tạo một đối tượng của các đối tượng duy nhất, một bộ. Tôi đã có ý tưởng tuyệt vời khi chỉ sử dụng một đối tượng JavaScript với các đối tượng cho các tên thuộc tính. Nhu la,

set[obj] = true;

Điều này hoạt động, lên đến một điểm. Nó hoạt động tuyệt vời với chuỗi và số, nhưng với các đối tượng khác, tất cả chúng dường như "băm" đến cùng một giá trị và truy cập vào cùng một thuộc tính. Có cách nào để tôi có thể tạo giá trị băm duy nhất cho một đối tượng không? Làm thế nào để chuỗi và số làm điều đó, tôi có thể ghi đè hành vi tương tự không?


32
Lý do tất cả các đối tượng 'băm' đến cùng một giá trị là do bạn chưa ghi đè các phương thức toString của chúng. Vì các khóa được yêu cầu là chuỗi, nên phương thức toString được gọi tự động để lấy khóa hợp lệ để tất cả các đối tượng của bạn được chuyển đổi thành cùng một chuỗi mặc định: "[Object Object]".
alanning

4
JSON.stringify(obj)hoặc obj.toSource()có thể làm việc cho bạn tùy thuộc vào vấn đề và nền tảng đích.
AnnanFay

4
@Annan JSON.opesify (obj) theo nghĩa đen chỉ cần chuyển đổi đối tượng (toàn bộ) thành một chuỗi. Vì vậy, về cơ bản bạn sẽ chỉ sao chép đối tượng vào chính nó. Điều này là vô nghĩa, lãng phí không gian và không tối ưu.
Metalstorm

1
@Metalstorm Đúng, đó là lý do tại sao nó phụ thuộc vào vấn đề của bạn. Khi tôi tìm thấy câu hỏi này thông qua google, giải pháp cuối cùng của tôi là gọi toSource () trên các đối tượng. Một phương pháp khác chỉ là sử dụng hàm băm thông thường trên nguồn.
AnnanFay

@ Nam, toSourcekhông hoạt động trong Chrome btw
Pacerier

Câu trả lời:


35

Các đối tượng JavaScript chỉ có thể sử dụng chuỗi làm khóa (mọi thứ khác được chuyển đổi thành chuỗi).

Ngoài ra, bạn có thể duy trì một mảng lập chỉ mục các đối tượng trong câu hỏi và sử dụng chuỗi chỉ mục của nó làm tham chiếu đến đối tượng. Một cái gì đó như thế này:

var ObjectReference = [];
ObjectReference.push(obj);

set['ObjectReference.' + ObjectReference.indexOf(obj)] = true;

Rõ ràng đó là một chút dài dòng, nhưng bạn có thể viết một vài phương thức xử lý nó và nhận và thiết lập tất cả willy nilly.

Biên tập:

Dự đoán của bạn là thực tế - đây là hành vi được xác định trong JavaScript - cụ thể là chuyển đổi toString xảy ra có nghĩa là bạn có thể xác định hàm toString của riêng mình trên đối tượng sẽ được sử dụng làm tên thuộc tính. - olliej

Điều này mang đến một điểm thú vị khác; bạn có thể định nghĩa một phương thức toString trên các đối tượng bạn muốn băm và điều đó có thể tạo thành định danh băm của chúng.


một tùy chọn khác là cung cấp cho mỗi đối tượng một giá trị ngẫu nhiên khi nó băm - có thể là số ngẫu nhiên + tổng số dấu - sau đó có một tập hợp các hàm để thêm / xóa đối tượng khỏi mảng.
Sugendran

4
Điều này sẽ thất bại nếu bạn thêm cùng một đối tượng hai lần. Nó sẽ nghĩ rằng nó là khác nhau.
Daniel X Moore

"Điều này sẽ thất bại nếu bạn thêm cùng một đối tượng hai lần. Nó sẽ nghĩ rằng nó khác nhau." Điểm tốt. Một giải pháp có thể là phân lớp Array cho ObjectReference, nối một kiểm tra trùng lặp vào push (). Bây giờ tôi không có thời gian để chỉnh sửa giải pháp này, nhưng tôi hy vọng tôi sẽ nhớ sau.
mí mắt

8
Tôi thích giải pháp này, vì nó không cần bất kỳ thuộc tính bổ sung nào trong đối tượng. Nhưng nó đang trở nên có vấn đề, nếu bạn cố gắng có một bộ thu gom rác sạch. Theo cách tiếp cận của bạn, nó sẽ lưu đối tượng mặc dù các tham chiếu khác đã bị xóa. Điều này có thể dẫn đến các vấn đề trong các ứng dụng lớn hơn.
Johnny

35
Điểm băm các đối tượng là gì nếu mỗi lần bạn tham khảo chúng, bạn cần quét tuyến tính một mảng?
Bordaigorl

57

Nếu bạn muốn hàm hashCode () giống như Java trong JavaScript, thì đó là của bạn:

String.prototype.hashCode = function(){
    var hash = 0;
    for (var i = 0; i < this.length; i++) {
        var character = this.charCodeAt(i);
        hash = ((hash<<5)-hash)+character;
        hash = hash & hash; // Convert to 32bit integer
    }
    return hash;
}

Đó là cách triển khai trong Java (toán tử bitwise).

Xin lưu ý rằng hashCode có thể dương và âm và điều đó là bình thường, hãy xem HashCode cho các giá trị âm . Vì vậy, bạn có thể xem xét để sử dụng Math.abs()cùng với chức năng này.


5
điều này tạo ra -hash, không hoàn hảo
qodeninja

2
@KimKha charlà từ dành riêng trong JS và có thể gây ra một số vấn đề. Một số tên khác sẽ tốt hơn.
szeryf

16
@qodeninja nói ai? Đây là lần đầu tiên tôi nghe một yêu cầu như vậy. Bạn có thể liên kết đến một số nguồn? Băm thường được tính toán bằng các phép toán số nguyên và bit số nguyên có kích thước cố định, do đó, việc có được kết quả dương hoặc âm là điều đáng được mong đợi.
szeryf

7
Kén chọn, nhưng ... "if (this.length == 0) return hash;" là dư thừa :) Và cá nhân sẽ thay đổi "ký tự" thành "mã".
Bão kim loại

10
@qodeninja và @szeryf: bạn chỉ cần cẩn thận với cách bạn sử dụng nó. Ví dụ, tôi đã cố gắng làm pickOne["helloo".hashCode() % 20]cho một mảng pickOnevới 20 phần tử. Tôi đã nhận được undefinedvì mã băm là âm, vì vậy đây là một ví dụ trong đó ai đó (tôi) mặc nhiên giả định mã băm dương.
Jim Pivarski

31

Cách dễ nhất để làm điều này là cung cấp cho mỗi đối tượng của bạn toStringphương thức riêng của nó :

(function() {
    var id = 0;

    /*global MyObject */
    MyObject = function() {
        this.objectId = '<#MyObject:' + (id++) + '>';
        this.toString= function() {
            return this.objectId;
        };
    };
})();

Tôi đã có cùng một vấn đề và điều này đã giải quyết nó một cách hoàn hảo cho tôi với sự phiền phức tối thiểu và dễ dàng hơn rất nhiều khi thực hiện lại một số kiểu Java béo Hashtablevà thêm equals()hashCode()vào các lớp đối tượng của bạn. Chỉ cần đảm bảo rằng bạn cũng không dán một chuỗi '<#MyObject: 12> vào hàm băm của mình hoặc nó sẽ xóa sạch mục nhập cho đối tượng thoát của bạn với id đó.

Bây giờ tất cả các băm của tôi là hoàn toàn lạnh. Tôi cũng vừa đăng một mục blog vài ngày trước về chủ đề chính xác này .


28
Nhưng điều này bỏ lỡ toàn bộ điểm. Java có equals()hashCode()do đó hai đối tượng tương đương có cùng giá trị băm. Sử dụng phương pháp trên có nghĩa là mọi phiên bản của MyObjectsẽ có một chuỗi duy nhất, điều đó có nghĩa là bạn sẽ phải giữ một tham chiếu đến đối tượng đó để lấy lại giá trị chính xác từ bản đồ. Có một chìa khóa là vô nghĩa, bởi vì nó không liên quan gì đến tính độc đáo của một đối tượng. Một toString()hàm hữu ích sẽ cần được triển khai cho loại đối tượng cụ thể mà bạn đang sử dụng làm khóa.
sethro

@sethro bạn có thể triển khai toStringcho các đối tượng sao cho nó trực tiếp ánh xạ tới một mối quan hệ tương đương sao cho hai đối tượng tạo ra cùng một chuỗi iff chúng được coi là "bằng nhau".
Daniel X Moore

3
Đúng, và đó là cách duy nhất đúng để sử dụng toString()để cho phép bạn sử dụng Objectnhư một Set. Tôi nghĩ rằng tôi đã hiểu nhầm câu trả lời của bạn là cố gắng cung cấp một giải pháp chung chung để tránh viết toString()tương đương equals()hoặc hashCode()theo từng trường hợp.
sethro

3
Hạ cấp. Đây không phải là mã băm, hãy xem câu trả lời của tôi về: stackoverflow.com/a/14953738/524126 Và một triển khai thực sự của
mã băm

5
@Metalstorm câu hỏi không hỏi về mã băm "thật", mà là làm thế nào để sử dụng thành công một đối tượng như một bộ trong JavaScript.
Daniel X Moore

20

Những gì bạn mô tả được bao phủ bởi Harmony WeakMaps , một phần của đặc tả ECMAScript 6 (phiên bản tiếp theo của JavaScript). Đó là: một tập hợp trong đó các khóa có thể là bất cứ thứ gì (bao gồm cả không xác định) và không thể đếm được.

Điều này có nghĩa là không thể có được tham chiếu đến một giá trị trừ khi bạn có tham chiếu trực tiếp đến khóa (bất kỳ đối tượng nào!) Liên kết đến nó. Điều quan trọng đối với một loạt các lý do triển khai động cơ liên quan đến hiệu quả và thu gom rác, nhưng nó cũng cực kỳ hay vì nó cho phép các ngữ nghĩa mới như quyền truy cập có thể thu hồi và truyền dữ liệu mà không làm lộ người gửi dữ liệu.

Từ MDN :

var wm1 = new WeakMap(),
    wm2 = new WeakMap();
var o1 = {},
    o2 = function(){},
    o3 = window;

wm1.set(o1, 37);
wm1.set(o2, "azerty");
wm2.set(o1, o2); // A value can be anything, including an object or a function.
wm2.set(o3, undefined);
wm2.set(wm1, wm2); // Keys and values can be any objects. Even WeakMaps!

wm1.get(o2); // "azerty"
wm2.get(o2); // Undefined, because there is no value for o2 on wm2.
wm2.get(o3); // Undefined, because that is the set value.

wm1.has(o2); // True
wm2.has(o2); // False
wm2.has(o3); // True (even if the value itself is 'undefined').

wm1.has(o1);   // True
wm1.delete(o1);
wm1.has(o1);   // False

WeakMaps có sẵn trong Firefox, Chrome và Edge hiện tại. Chúng cũng được hỗ trợ trong Node v7 và trong v6 với --harmony-weak-mapscờ.


1
Sự khác biệt giữa những điều này và là Mapgì?
smac89

@ smac89 WeakMap có các giới hạn: 1) Chỉ lấy các đối tượng làm khóa 2) Không có thuộc tính kích thước 3) Không có phương thức lặp hoặc forEach 4) Không có phương thức rõ ràng. Khóa là đối tượng - vì vậy khi đối tượng sẽ bị xóa khỏi bộ nhớ - dữ liệu từ WeakMap được kết nối với đối tượng này cũng sẽ bị xóa. Nó rất hữu ích khi chúng ta muốn giữ thông tin, chỉ tồn tại trong khi đối tượng tồn tại. Vì vậy, WeakMap chỉ có các phương thức: thiết lập, xóa để ghi và nhận, đã đọc
Ekaterina Tokareva

Điều này không chính xác hoạt động chính xác ... var m = new Map();m.set({},"abc"); console.log(m.get({}) //=>undefinedNó chỉ hoạt động nếu bạn có cùng một biến bạn đã tham chiếu ban đầu trong lệnh set. EGvar m = new Map();a={};m.set(a,"abc"); console.log(m.get(a) //=>undefined
Sancarn

1
@Sancarn Nó không phải là cùng một biến, nhưng chúng phải trỏ đến cùng một đối tượng. Trong ví dụ đầu tiên của bạn, bạn có hai đối tượng khác nhau, chúng trông giống nhau, nhưng chúng có địa chỉ khác nhau.
Svish

1
@Svish điểm tốt! Mặc dù tôi biết điều đó ngay bây giờ, nhưng có lẽ tôi đã không thực hiện được sau đó :)
Sancarn

19

Giải pháp tôi chọn tương tự như của Daniel, nhưng thay vì sử dụng một nhà máy sản xuất đối tượng và ghi đè lên toString, tôi rõ ràng thêm hàm băm vào đối tượng khi được yêu cầu lần đầu tiên thông qua hàm getHashCode. Một chút lộn xộn, nhưng tốt hơn cho nhu cầu của tôi :)

Function.prototype.getHashCode = (function(id) {
    return function() {
        if (!this.hashCode) {
            this.hashCode = '<hash|#' + (id++) + '>';
        }
        return this.hashCode;
    }
}(0));

7
Nếu bạn muốn đi theo con đường này, nó tốt hơn nhiều để thiết lập hashCode qua Object.definePropertyvới enumerablecác thiết lập để false, vì vậy bạn sẽ không sụp đổ bất kỳ for .. invòng lặp.
Sebastian Nowak

14

Đối với tình huống cụ thể của tôi, tôi chỉ quan tâm đến sự bình đẳng của đối tượng cho đến khi các khóa và giá trị nguyên thủy đi. Giải pháp hiệu quả với tôi là chuyển đổi đối tượng thành biểu diễn JSON của nó và sử dụng nó làm hàm băm. Có những hạn chế như thứ tự của định nghĩa khóa có khả năng không nhất quán; nhưng như tôi đã nói nó hoạt động với tôi vì những vật thể này đều được tạo ra ở một nơi.

var hashtable = {};

var myObject = {a:0,b:1,c:2};

var hash = JSON.stringify(myObject);
// '{"a":0,"b":1,"c":2}'

hashtable[hash] = myObject;
// {
//   '{"a":0,"b":1,"c":2}': myObject
// }

10

Tôi đã kết hợp một mô-đun JavaScript nhỏ trước đây để tạo mã băm cho chuỗi, đối tượng, mảng, v.v. (Tôi chỉ cam kết nó với GitHub :))

Sử dụng:

Hashcode.value("stackoverflow")
// -2559914341
Hashcode.value({ 'site' : "stackoverflow" })
// -3579752159

Không phải javascript của javascript trên tham chiếu vòng tròn quá?
Clayton Rabenda

@Ryan Long Tôi có thể nói rằng nếu bạn có tài liệu tham khảo vòng tròn thì bạn cần phải cấu trúc lại mã của mình;)
Metalstorm

11
@Metalstorm "thì bạn cần cấu trúc lại mã của mình" Bạn đang đùa à? Mỗi cặp cha mẹ và phần tử DOM tạo thành một tham chiếu vòng tròn.
Chris Middleton

8
Nó thực hiện một công việc băm nhỏ các đối tượng có thuộc tính số, trả về cùng một giá trị trong nhiều trường hợp, tức là var hash1 = Hashcode.value({ a: 1, b: 2 }); var hash2 = Hashcode.value({ a: 2, b: 1 }); console.log(hash1, hash2);sẽ đăng nhập2867874173 2867874173
Julien Bérubé 17/03/2015

9

Đặc tả JavaScript xác định quyền truy cập thuộc tính được lập chỉ mục là thực hiện chuyển đổi toString trên tên chỉ mục. Ví dụ,

myObject[myProperty] = ...;

giống như

myObject[myProperty.toString()] = ...;

Điều này là cần thiết như trong JavaScript

myObject["someProperty"]

giống như

myObject.someProperty

Và vâng, nó cũng làm tôi buồn :-(


9

Trong ECMAScript 6 bây giờ có một Setcách hoạt động như bạn muốn: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/set

Nó đã có sẵn trong Chrome, FF và IE11 mới nhất.


1
Đây sẽ là câu trả lời hàng đầu trong năm 2016. Nếu bạn sử dụng Babel, bạn có thể sử dụng Set theo thông số ES6 và nó sẽ được tự động điền vào đầu ra ES5. babeljs.io/docs/learn-es2015/#map-set-weak-map-weak-set
atroberts20

5

Tham khảo: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol

bạn có thể sử dụng biểu tượng Es6 để tạo đối tượng truy cập và khóa duy nhất. Mỗi giá trị biểu tượng được trả về từ Symbol () là duy nhất. Một giá trị ký hiệu có thể được sử dụng làm định danh cho các thuộc tính đối tượng; đây là mục đích duy nhất của kiểu dữ liệu

var obj = {};

obj[Symbol('a')] = 'a';
obj[Symbol.for('b')] = 'b';
obj['c'] = 'c';
obj.d = 'd';

2
Ngoại trừ việc thực sự không có cách nào để tạo lại Biểu tượng, hãy x = Biểu tượng ('a'); let y = Symbol ('a'); console.log (x === y); // trả về false Vì vậy, Symbol không hoạt động như hàm băm.
Richard Collette

3

Đây là giải pháp đơn giản của tôi trả về một số nguyên duy nhất.

function hashcode(obj) {
    var hc = 0;
    var chars = JSON.stringify(obj).replace(/\{|\"|\}|\:|,/g, '');
    var len = chars.length;
    for (var i = 0; i < len; i++) {
        // Bump 7 to larger prime number to increase uniqueness
        hc += (chars.charCodeAt(i) * 7);
    }
    return hc;
}

2
Sự phức tạp của điều này làm suy yếu toàn bộ ý tưởng đằng sau hashCode ()
tuxSlayer

Tôi không thấy điều này phức tạp không cần thiết. Tôi đã tò mò, mặc dù: tại sao giai đoạn thay thế? Các loại trừ sẽ trả lại tiền phạt trên charCodeAt, phải không?
Greg Pettit

Quá tệ vì hashcode({a:1, b:2}) === hashcode({a:2, b:1})và nhiều xung đột khác.
maaartinus

3

Dựa trên tiêu đề, chúng ta có thể tạo các giá trị băm mạnh với js, nó có thể được sử dụng để tạo ra một hàm băm duy nhất từ ​​một đối tượng, một mảng các thông số, một chuỗi hoặc bất cứ thứ gì.

Sau này để lập chỉ mục, điều này tránh mọi lỗi khớp có thể xảy ra, đồng thời cho phép truy xuất chỉ mục từ các thông số (tránh tìm kiếm / lặp đối tượng, v.v.):

async function H(m) {
  const msgUint8 = new TextEncoder().encode(m)                       
  const hashBuffer = await crypto.subtle.digest('SHA-256', msgUint8)          
  const hashArray = Array.from(new Uint8Array(hashBuffer))                    
  const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('')
  console.log(hashHex)
}

/* Examples ----------------------- */
H("An obscure ....")
H(JSON.stringify( {"hello" : "world"} ))
H(JSON.stringify( [54,51,54,47] ))

Đầu ra ở trên trong trình duyệt của tôi, nó cũng phải bằng với bạn ( Có thật không? ):

bf1cf3fe6975fe382ab392ec1dd42009380614be03d489f23601c11413cfca2b
93a23971a914e5eacbf0a8d25154cda309c3c1c72fbb9914d47c60f3cb681588
d2f209e194045604a3b15bdfd7502898a0e848e4603c5a818bd01da69c00ad19

https://developer.mozilla.org/en-US/docs/Web/API/SubussyCrypto/digest#Converting_a_digest_to_a_hex_opes


1

Giải pháp của tôi giới thiệu một hàm tĩnh cho Objectđối tượng toàn cầu .

(function() {
    var lastStorageId = 0;

    this.Object.hash = function(object) {
        var hash = object.__id;

        if (!hash)
             hash = object.__id = lastStorageId++;

        return '#' + hash;
    };
}());

Tôi nghĩ rằng điều này thuận tiện hơn với các hàm thao tác đối tượng khác trong JavaScript.


1
Các đối tượng có cùng giá trị nội bộ sẽ băm vào các giá trị băm khác nhau, đây không phải là điều mà hàm băm (mã) thực hiện.
Metalstorm

Trong JavaScript (và tôi nghĩ trong hầu hết các ngôn ngữ khác cũng vậy) hai đối tượng được tạo với cùng giá trị bên trong vẫn là các đối tượng khác nhau, bởi vì kiểu dữ liệu lót được đại diện bởi một đối tượng mới. jsfiddle.net/h4G9f
Johnny

4
Có nhưng đó không phải là mã băm để làm gì, mã băm được sử dụng để kiểm tra sự bằng nhau của trạng thái đối tượng. Giống như hàm băm, cùng một đầu vào đi vào (giá trị biến) cùng một hàm băm xuất hiện. Những gì bạn đang tìm kiếm là một UUID (đó là những gì chức năng của bạn cung cấp).
Metalstorm

1
Bạn đúng. Tôi hiểu sai câu hỏi. Thực sự xấu, rằng câu trả lời được chấp nhận cũng không cung cấp một giải pháp tốt.
Johnny

Cũng đảm nhận chức năng của bạn, tôi sẽ nghiêng về nó giống như thế này: jsfiddle.net/xVSsd Kết quả tương tự, ngắn hơn (LoC + chars), và có lẽ nhanh hơn một chút xíu :)
Metalstorm

1

Tôi sẽ cố gắng đi sâu hơn một chút so với các câu trả lời khác.

Ngay cả khi JS có hỗ trợ băm tốt hơn, nó sẽ không băm hoàn hảo mọi thứ một cách hoàn hảo, trong nhiều trường hợp, bạn sẽ phải xác định hàm băm của riêng mình. Ví dụ Java có hỗ trợ băm tốt, nhưng bạn vẫn phải suy nghĩ và thực hiện một số công việc.

Một vấn đề là với thuật ngữ băm / mã băm ... có băm mật mã và băm không mã hóa. Vấn đề khác, là bạn phải hiểu tại sao băm là hữu ích và làm thế nào nó hoạt động.

Khi chúng ta nói về băm trong JavaScript hoặc Java, chúng ta thường nói về băm không mã hóa, thường là về băm cho hashmap / hashtable (trừ khi chúng ta đang làm việc trên xác thực hoặc mật khẩu, bạn có thể thực hiện phía máy chủ bằng NodeJS. ..).

Nó phụ thuộc vào dữ liệu bạn có và những gì bạn muốn đạt được.

Dữ liệu của bạn có một số tính duy nhất "đơn giản" tự nhiên:

  • Giá trị băm của một số nguyên là ... số nguyên, vì nó là duy nhất, may mắn cho bạn!
  • Hàm băm của một chuỗi ... nó phụ thuộc vào chuỗi, nếu chuỗi đại diện cho một mã định danh duy nhất, bạn có thể coi đó là một hàm băm (vì vậy không cần băm).
  • Bất cứ điều gì gián tiếp khá nhiều một số nguyên duy nhất là trường hợp đơn giản nhất
  • Điều này sẽ tôn trọng: mã băm bằng nhau nếu các đối tượng bằng nhau

Dữ liệu của bạn có một số tính duy nhất "tổng hợp" tự nhiên:

  • Ví dụ: với một đối tượng người, bạn có thể tính toán hàm băm bằng cách sử dụng tên, họ, ngày sinh, ... xem cách Java thực hiện: Hàm băm tốt cho chuỗi hoặc sử dụng một số thông tin ID khác đủ rẻ và duy nhất cho usecase của bạn

Bạn không biết dữ liệu của bạn sẽ là gì:

  • Chúc may mắn ... bạn có thể tuần tự hóa chuỗi và băm theo kiểu Java, nhưng điều đó có thể tốn kém nếu chuỗi lớn và nó sẽ không tránh được va chạm cũng như nói hàm băm của một số nguyên (tự).

Không có kỹ thuật băm hiệu quả kỳ diệu cho dữ liệu chưa biết, trong một số trường hợp khá dễ dàng, trong những trường hợp khác bạn có thể phải suy nghĩ hai lần. Vì vậy, ngay cả khi JavaScript / ECMAScript thêm hỗ trợ, không có giải pháp ngôn ngữ kỳ diệu nào cho vấn đề này.

Trong thực tế, bạn cần hai điều: đủ độc đáo, đủ tốc độ

Thêm vào đó, thật tuyệt khi có: "mã băm bằng nhau nếu các đối tượng bằng nhau"


0

Nếu bạn thực sự muốn thiết lập hành vi (theo kiến ​​thức Java), thì bạn sẽ khó tìm được giải pháp trong JavaScript. Hầu hết các nhà phát triển sẽ đề xuất một khóa duy nhất để đại diện cho từng đối tượng, nhưng điều này không giống với tập hợp, trong đó bạn có thể nhận được hai đối tượng giống hệt nhau với một khóa duy nhất. API Java thực hiện công việc kiểm tra các giá trị trùng lặp bằng cách so sánh các giá trị mã băm, không phải các khóa và do không có biểu diễn giá trị mã băm của các đối tượng trong JavaScript, nên gần như không thể làm như vậy. Ngay cả thư viện Prototype JS cũng thừa nhận thiếu sót này, khi nó nói:

"Hash có thể được coi là một mảng kết hợp, liên kết các khóa duy nhất với các giá trị (không nhất thiết là duy nhất) ..."

http://www.prototypejs.org/api/hash


0

Ngoài câu trả lời của mí mắt, đây là một hàm trả về ID duy nhất có thể lặp lại cho bất kỳ đối tượng nào:

var uniqueIdList = [];
function getConstantUniqueIdFor(element) {
    // HACK, using a list results in O(n), but how do we hash e.g. a DOM node?
    if (uniqueIdList.indexOf(element) < 0) {
        uniqueIdList.push(element);
    }
    return uniqueIdList.indexOf(element);
}

Như bạn có thể thấy nó sử dụng một danh sách để tra cứu rất kém hiệu quả, tuy nhiên đó là cách tốt nhất tôi có thể tìm thấy bây giờ.


0

Nếu bạn muốn sử dụng các đối tượng làm khóa, bạn cần ghi đè Phương thức toString của chúng, như một số đã được đề cập ở đây. Các hàm băm đã được sử dụng đều ổn, nhưng chúng chỉ hoạt động cho cùng một đối tượng chứ không phải cho các đối tượng bằng nhau.

Tôi đã viết một thư viện nhỏ tạo băm từ các đối tượng, mà bạn có thể dễ dàng sử dụng cho mục đích này. Các đối tượng thậm chí có thể có một thứ tự khác nhau, các giá trị băm sẽ giống nhau. Trong nội bộ, bạn có thể sử dụng các loại khác nhau cho hàm băm của mình (djb2, md5, sha1, sha256, sha512, ripemd160).

Đây là một ví dụ nhỏ từ tài liệu:

var hash = require('es-hash');

// Save data in an object with an object as a key
Object.prototype.toString = function () {
    return '[object Object #'+hash(this)+']';
}

var foo = {};

foo[{bar: 'foo'}] = 'foo';

/*
 * Output:
 *  foo
 *  undefined
 */
console.log(foo[{bar: 'foo'}]);
console.log(foo[{}]);

Gói này có thể được sử dụng trong trình duyệt và trong Node-Js.

Kho lưu trữ: https://bitbucket.org/tehrengruber/es-js-hash


0

Nếu bạn muốn có các giá trị duy nhất trong một đối tượng tra cứu, bạn có thể làm một cái gì đó như thế này:

Tạo một đối tượng tra cứu

var lookup = {};

Thiết lập chức năng mã băm

function getHashCode(obj) {
    var hashCode = '';
    if (typeof obj !== 'object')
        return hashCode + obj;
    for (var prop in obj) // No hasOwnProperty needed
        hashCode += prop + getHashCode(obj[prop]); // Add key + value to the result string
    return hashCode;
}

Vật

var key = getHashCode({ 1: 3, 3: 7 });
// key = '1337'
lookup[key] = true;

Mảng

var key = getHashCode([1, 3, 3, 7]);
// key = '01132337'
lookup[key] = true;

Các loại khác

var key = getHashCode('StackOverflow');
// key = 'StackOverflow'
lookup[key] = true;

Kết quả cuối cùng

{ 1337: true, 01132337: true, StackOverflow: true }

Lưu ý rằng getHashCodekhông trả về bất kỳ giá trị nào khi đối tượng hoặc mảng trống

getHashCode([{},{},{}]);
// '012'
getHashCode([[],[],[]]);
// '012'

Điều này tương tự như giải pháp @ijmacd chỉ getHashCodekhông có sự JSONphụ thuộc.


Bạn phải có một vấn đề với các tham chiếu vòng tròn với điều đó
tuxSlayer

@tuxSlayer Cảm ơn đã cho tôi biết. Bạn có thể dễ dàng mở rộng mã này với nhu cầu của mình nhưng tôi hy vọng ý tưởng này có phần rõ ràng :)
A1rPun

Điều này sẽ tạo ra các phím rất dài cho các vật thể lớn có thể ảnh hưởng đến bộ nhớ và hiệu năng khá khó khăn
Gershom

0

Tôi đã kết hợp các câu trả lời từ mí mắt và KimKha.

Sau đây là dịch vụ angularjs và nó hỗ trợ số, chuỗi và đối tượng.

exports.Hash = () => {
  let hashFunc;
  function stringHash(string, noType) {
    let hashString = string;
    if (!noType) {
      hashString = `string${string}`;
    }
    var hash = 0;
    for (var i = 0; i < hashString.length; i++) {
        var character = hashString.charCodeAt(i);
        hash = ((hash<<5)-hash)+character;
        hash = hash & hash; // Convert to 32bit integer
    }
    return hash;
  }

  function objectHash(obj, exclude) {
    if (exclude.indexOf(obj) > -1) {
      return undefined;
    }
    let hash = '';
    const keys = Object.keys(obj).sort();
    for (let index = 0; index < keys.length; index += 1) {
      const key = keys[index];
      const keyHash = hashFunc(key);
      const attrHash = hashFunc(obj[key], exclude);
      exclude.push(obj[key]);
      hash += stringHash(`object${keyHash}${attrHash}`, true);
    }
    return stringHash(hash, true);
  }

  function Hash(unkType, exclude) {
    let ex = exclude;
    if (ex === undefined) {
      ex = [];
    }
    if (!isNaN(unkType) && typeof unkType !== 'string') {
      return unkType;
    }
    switch (typeof unkType) {
      case 'object':
        return objectHash(unkType, ex);
      default:
        return stringHash(String(unkType));
    }
  }

  hashFunc = Hash;

  return Hash;
};

Cách sử dụng ví dụ:

Hash('hello world'), Hash('hello world') == Hash('hello world')
Hash({hello: 'hello world'}), Hash({hello: 'hello world'}) == Hash({hello: 'hello world'})
Hash({hello: 'hello world', goodbye: 'adios amigos'}), Hash({hello: 'hello world', goodbye: 'adios amigos'}) == Hash({goodbye: 'adios amigos', hello: 'hello world'})
Hash(['hello world']), Hash(['hello world']) == Hash(['hello world'])
Hash(1), Hash(1) == Hash(1)
Hash('1'), Hash('1') == Hash('1')

Đầu ra

432700947 true
-411117486 true
1725787021 true
-1585332251 true
1 true
-1881759168 true

Giải trình

Như bạn có thể thấy trung tâm của dịch vụ là hàm băm do KimKha tạo ra. Tôi đã thêm các loại vào chuỗi để cấu trúc của đối tượng cũng sẽ tác động đến giá trị băm cuối cùng. Các khóa được băm để ngăn va chạm mảng | đối tượng.

so sánh đối tượng mí mắt được sử dụng để ngăn ngừa đệ quy vô hạn bằng cách tự tham chiếu các đối tượng.

Sử dụng

Tôi đã tạo dịch vụ này để tôi có thể có một dịch vụ lỗi được truy cập với các đối tượng. Vì vậy, một dịch vụ có thể đăng ký lỗi với một đối tượng nhất định và dịch vụ khác có thể xác định xem có lỗi nào được tìm thấy không.

I E

JsonValidation.js

ErrorSvc({id: 1, json: '{attr: "not-valid"}'}, 'Invalid Json Syntax - key not double quoted');

UserOfData.js

ErrorSvc({id: 1, json: '{attr: "not-valid"}'});

Điều này sẽ trở lại:

['Invalid Json Syntax - key not double quoted']

Trong khi

ErrorSvc({id: 1, json: '{"attr": "not-valid"}'});

Điều này sẽ trở lại

[]

0

Chỉ cần sử dụng tài sản bí mật ẩn với defineProperty enumerable: false

Nó hoạt động rất nhanh :

  • Lần đọc duy nhất đầu tiên: 1.257.500 ops / s
  • Tất cả những người khác: 309.226.485 ops / s
var nextObjectId = 1
function getNextObjectId() {
    return nextObjectId++
}

var UNIQUE_ID_PROPERTY_NAME = '458d576952bc489ab45e98ac7f296fd9'
function getObjectUniqueId(object) {
    if (object == null) {
        return null
    }

    var id = object[UNIQUE_ID_PROPERTY_NAME]

    if (id != null) {
        return id
    }

    if (Object.isFrozen(object)) {
        return null
    }

    var uniqueId = getNextObjectId()
    Object.defineProperty(object, UNIQUE_ID_PROPERTY_NAME, {
        enumerable: false,
        configurable: false,
        writable: false,
        value: uniqueId,
    })

    return uniqueId
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.