Apakah ada fungsi kode hash dalam JavaScript?


150

Pada dasarnya, saya mencoba membuat objek objek unik, satu set. Saya memiliki ide cemerlang hanya menggunakan objek JavaScript dengan objek untuk nama properti. Seperti,

set[obj] = true;

Ini berfungsi, sampai batas tertentu. Ini berfungsi baik dengan string dan angka, tetapi dengan objek lain, mereka semua tampaknya "hash" dengan nilai yang sama dan mengakses properti yang sama. Apakah ada semacam cara saya bisa menghasilkan nilai hash unik untuk suatu objek? Bagaimana string dan angka melakukannya, dapatkah saya mengesampingkan perilaku yang sama?


32
Alasan semua objek 'hash' dengan nilai yang sama adalah karena Anda belum mengganti metode toString mereka. Karena kunci harus berupa string, metode toString secara otomatis dipanggil untuk mendapatkan kunci yang valid sehingga semua objek Anda dikonversi ke string default yang sama: "[objek objek]".
alanning

4
JSON.stringify(obj)atau obj.toSource()dapat bekerja untuk Anda tergantung pada platform masalah dan target.
AnnanFay

4
@Annan JSON.stringify (obj) secara harfiah hanya mengubah objek (keseluruhan) menjadi string. Jadi pada dasarnya Anda hanya menyalin objek ke dirinya sendiri. Ini tidak ada gunanya, buang-buang ruang dan tidak optimal.
Metalstorm

1
@ Logam Benar, itulah sebabnya itu tergantung apa masalah Anda. Ketika saya menemukan pertanyaan ini melalui google, solusi terakhir saya adalah memanggil toSource () pada objek. Metode lain hanya menggunakan hash konvensional pada sumbernya.
AnnanFay

@Annan, toSourcejangan bekerja di Chrome btw
Pacerier

Jawaban:


35

Objek JavaScript hanya dapat menggunakan string sebagai kunci (apa pun yang dikonversi ke string).

Anda bisa, sebagai alternatif, mempertahankan array yang mengindeks objek yang dimaksud, dan menggunakan string indeks sebagai referensi ke objek. Sesuatu seperti ini:

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

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

Jelas itu sedikit bertele-tele, tetapi Anda bisa menulis beberapa metode yang menanganinya dan mendapatkan dan mengatur semua mau tak mau.

Edit:

Dugaan Anda adalah fakta - ini adalah perilaku yang didefinisikan dalam JavaScript - khususnya konversi toString terjadi yang berarti Anda dapat menentukan fungsi toString Anda sendiri pada objek yang akan digunakan sebagai nama properti. - olliej

Ini memunculkan poin menarik lainnya; Anda dapat mendefinisikan metode toString pada objek yang ingin Anda hash, dan yang dapat membentuk pengidentifikasi hash mereka.


pilihan lain adalah memberikan setiap objek nilai acak karena hash - mungkin angka acak + kutu total - kemudian memiliki satu set fungsi untuk menambah / menghapus objek dari array.
Sugendran

4
Ini akan gagal jika Anda menambahkan objek yang sama dua kali. Ia akan berpikir bahwa itu berbeda.
Daniel X Moore

"Ini akan gagal jika kamu menambahkan objek yang sama dua kali. Itu akan berpikir bahwa itu berbeda." Poin yang bagus. Sebuah solusi bisa dengan subclass Array untuk ObjectReference, mengaitkan cek duplikat ke push (). Saya tidak punya waktu untuk mengedit solusi ini sekarang, tapi saya harap saya akan ingat nanti.
kelopak mata

8
Saya suka solusi ini, karena tidak memerlukan properti tambahan di objek. Tapi semakin bermasalah, jika Anda mencoba memiliki pengumpul sampah yang bersih. Dalam pendekatan Anda itu akan menyimpan objek meskipun referensi lain sudah dihapus. Ini dapat menyebabkan masalah pada aplikasi yang lebih besar.
Johnny

35
Apa gunanya hashing objek jika setiap kali Anda merujuknya Anda memerlukan pemindaian linear array?
Bordaigorl

57

Jika Anda ingin fungsi hashCode () seperti Java di JavaScript, itu milik Anda:

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;
}

Itulah cara implementasi di Java (operator bitwise).

Harap perhatikan bahwa kode hash bisa positif dan negatif, dan itu normal, lihat HashCode memberikan nilai negatif . Jadi, Anda bisa mempertimbangkan untuk menggunakan Math.abs()fungsi ini.


5
ini menciptakan -hash, tidak sempurna
qodeninja

2
@ KimKha chardicadangkan kata dalam JS dan dapat menyebabkan beberapa masalah. Beberapa nama lain akan lebih baik.
szeryf

16
@qodeninja berkata siapa? Ini pertama kalinya saya mendengar klaim seperti itu. Bisakah Anda menautkan ke beberapa sumber? Hash biasanya dihitung menggunakan aritmatika integer ukuran tetap dan operasi bit, sehingga mendapatkan hasil positif atau negatif adalah sesuatu yang diharapkan.
szeryf

7
Pilih-pilih, tapi ... "if (this.length == 0) mengembalikan hash;" redundan :) Dan secara pribadi akan mengubah "karakter" menjadi "kode".
Metalstorm

10
@qodeninja dan @szeryf: Anda hanya harus berhati-hati dalam menggunakannya. Sebagai contoh, saya coba lakukan pickOne["helloo".hashCode() % 20]untuk array pickOnedengan 20 elemen. Saya dapatkan undefinedkarena kode hash negatif, jadi ini adalah contoh di mana seseorang (saya) secara implisit mengasumsikan kode hash positif.
Jim Pivarski

31

Cara termudah untuk melakukan ini adalah memberikan masing-masing objek Anda toStringmetode unik sendiri :

(function() {
    var id = 0;

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

Saya memiliki masalah yang sama dan ini menyelesaikannya dengan sempurna bagi saya dengan kerepotan minimal, dan jauh lebih mudah untuk menerapkan kembali beberapa gaya Java yang berlemak Hashtabledan menambahkan equals()dan hashCode()ke kelas objek Anda. Pastikan saja Anda tidak menempelkan string '<#MyObject: 12> ke hash Anda atau itu akan menghapus entri untuk objek Anda yang keluar dengan id itu.

Sekarang semua hash saya benar-benar dingin. Saya juga baru saja memposting entri blog beberapa hari yang lalu tentang topik yang tepat ini .


28
Tapi ini melewatkan seluruh intinya. Java memiliki equals()dan hashCode()sehingga dua objek yang setara memiliki nilai hash yang sama. Menggunakan metode di atas berarti bahwa setiap instance MyObjectakan memiliki string unik, yang berarti Anda harus menyimpan referensi ke objek tersebut untuk mendapatkan nilai yang benar dari peta. Memiliki kunci tidak ada artinya, karena tidak ada hubungannya dengan keunikan suatu objek. toString()Fungsi yang berguna perlu diimplementasikan untuk jenis objek tertentu yang Anda gunakan sebagai kunci.
sethro

@sethro Anda dapat mengimplementasikan objek toStringuntuk secara langsung memetakan ke relasi ekivalensi sehingga dua objek membuat string yang sama jika mereka dianggap "sama".
Daniel X Moore

3
Benar, dan itulah satu - satunya cara yang benar untuk digunakan toString()agar Anda dapat menggunakan Objectsebagai a Set. Saya pikir saya salah memahami jawaban Anda sebagai mencoba memberikan solusi umum untuk menghindari penulisan yang toString()setara equals()atau hashCode()berdasarkan kasus per kasus.
sethro

3
Terpilih. Ini bukan apa kode hash, lihat jawaban saya untuk: stackoverflow.com/a/14953738/524126 Dan implementasi sebenarnya dari kode hash: stackoverflow.com/a/15868654/524126
Metalstorm

5
@Metalstorm pertanyaan tidak bertanya tentang kode hash "benar", melainkan bagaimana cara berhasil menggunakan objek sebagai set di JavaScript.
Daniel X Moore

20

Apa yang Anda jelaskan dicakup oleh Harmony WeakMaps , bagian dari spesifikasi ECMAScript 6 (versi JavaScript berikutnya). Yaitu: set di mana kunci dapat berupa apa saja (termasuk yang tidak ditentukan) dan tidak dapat dihitung.

Ini berarti tidak mungkin untuk mendapatkan referensi ke nilai kecuali Anda memiliki referensi langsung ke kunci (objek apa pun!) Yang tertaut ke sana. Ini penting untuk banyak alasan implementasi mesin yang berkaitan dengan efisiensi dan pengumpulan sampah, tetapi juga sangat keren karena memungkinkan semantik baru seperti izin akses yang dapat dibatalkan dan mengirimkan data tanpa mengekspos pengirim data.

Dari 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 tersedia di Firefox, Chrome, dan Edge saat ini. Mereka juga didukung di Node v7, dan di v6 dengan --harmony-weak-mapsbendera.


1
Apa perbedaan antara ini dan Map?
smac89

@ smac89 WeakMap memiliki batasan: 1) Hanya mengambil objek sebagai kunci 2) Tanpa properti ukuran 3) Tidak ada metode iterator atau forEach 4) Tidak ada metode yang jelas. Kunci adalah objek - jadi ketika objek akan dihapus dari memori - data dari WeakMap yang terhubung dengan objek ini akan dihapus juga. Ini sangat berguna ketika kita ingin menyimpan informasi, yang seharusnya hanya ada saat objek ada. Jadi WeakMap hanya memiliki metode: set, delete untuk write dan get,
have

Ini tidak benar-benar berfungsi dengan benar ... var m = new Map();m.set({},"abc"); console.log(m.get({}) //=>undefinedIni hanya berfungsi jika Anda memiliki variabel yang sama dengan yang awalnya Anda rujuk dalam perintah yang ditetapkan. EGvar m = new Map();a={};m.set(a,"abc"); console.log(m.get(a) //=>undefined
Sancarn

1
@ Canancarn Tidak harus variabel yang sama, tetapi mereka harus menunjuk ke objek yang sama. Dalam contoh pertama Anda, Anda memiliki dua objek yang berbeda, mereka terlihat sama, tetapi mereka memiliki alamat yang berbeda.
Svish

1
@Svish tempat yang bagus! Meskipun saya tahu sekarang, saya mungkin tidak melakukannya saat itu :)
Sancarn

19

Solusi yang saya pilih mirip dengan Daniel, tetapi daripada menggunakan objek pabrik dan menimpa toString, saya secara eksplisit menambahkan hash ke objek ketika pertama kali diminta melalui fungsi getHashCode. Sedikit berantakan, tapi lebih baik untuk kebutuhan saya :)

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

7
Jika Anda ingin pergi dengan cara ini, jauh lebih baik untuk mengatur kode hash melalui Object.definePropertydengan enumerableset ke false, sehingga Anda tidak akan crash for .. inloop.
Sebastian Nowak

14

Untuk situasi spesifik saya, saya hanya peduli tentang kesetaraan objek sejauh kunci dan nilai-nilai primitif pergi. Solusi yang berhasil bagi saya adalah mengubah objek ke representasi JSON dan menggunakannya sebagai hash. Ada batasan seperti urutan definisi kunci yang berpotensi tidak konsisten; tapi seperti yang saya katakan itu bekerja untuk saya karena semua benda ini dihasilkan di satu tempat.

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

Saya mengumpulkan modul JavaScript kecil beberapa waktu lalu untuk menghasilkan kode hash untuk string, objek, array, dll. (Saya baru saja berkomitmen untuk GitHub :))

Pemakaian:

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

Bukankah javascript GC tersedak pada referensi melingkar juga?
Clayton Rabenda

@Ryan Long Saya mungkin lebih jauh mengatakan jika Anda memiliki referensi melingkar maka Anda perlu refactor kode Anda;)
Metalstorm

11
@ Metalstorm "maka Anda perlu refactor kode Anda" Apakah Anda bercanda? Setiap elemen orang tua dan pasangan anak DOM merupakan referensi melingkar.
Chris Middleton

8
Itu melakukan pekerjaan hashing objek yang buruk yang memiliki sifat numerik, mengembalikan nilai yang sama dalam banyak kasus, yaitu var hash1 = Hashcode.value({ a: 1, b: 2 }); var hash2 = Hashcode.value({ a: 2, b: 1 }); console.log(hash1, hash2);akan log2867874173 2867874173
Julien Bérubé

9

Spesifikasi JavaScript mendefinisikan akses properti yang diindeks sebagai melakukan konversi toString pada nama indeks. Sebagai contoh,

myObject[myProperty] = ...;

sama dengan

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

Ini diperlukan seperti dalam JavaScript

myObject["someProperty"]

sama dengan

myObject.someProperty

Dan ya, itu membuatku sedih juga :-(



5

Referensi: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol

Anda dapat menggunakan simbol Es6 untuk membuat kunci unik dan mengakses objek. Setiap nilai simbol yang dikembalikan dari Symbol () unik. Nilai simbol dapat digunakan sebagai pengidentifikasi untuk properti objek; ini adalah satu-satunya tujuan tipe data.

var obj = {};

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

2
Kecuali bahwa sebenarnya tidak ada cara untuk membuat kembali Simbol biarkan x = Simbol ('a'); biarkan y = Simbol ('a'); console.log (x === y); // mengembalikan false Jadi Simbol tidak bekerja seperti hash.
Richard Collette

3

Inilah solusi sederhana saya yang mengembalikan integer unik.

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
Kompleksitas ini merusak seluruh gagasan di balik hashCode ()
tuxSlayer

Saya tidak menemukan ini rumit yang tidak perlu. Saya penasaran, mengapa: fase penggantian? Pengecualian akan dikembalikan baik di charCodeAt, bukan?
Greg Pettit

Sayang sekali karena hashcode({a:1, b:2}) === hashcode({a:2, b:1})dan banyak konflik lainnya.
maaartinus

3

Berdasarkan pada judul, kita dapat menghasilkan hash yang kuat dengan js, dapat digunakan untuk menghasilkan hash unik dari suatu objek, array params, string, atau apa pun.

Kemudian untuk pengindeksan ini hindari kesalahan pencocokan yang memungkinkan, sambil memungkinkan untuk mengambil indeks dari params (hindari mencari / mengulang objek, dll.):

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] ))

Output di atas di browser saya, itu harus sama untuk Anda juga ( Benarkah? ):

bf1cf3fe6975fe382ab392ec1dd42009380614be03d489f23601c11413cfca2b
93a23971a914e5eacbf0a8d25154cda309c3c1c72fbb9914d47c60f3cb681588
d2f209e194045604a3b15bdfd7502898a0e848e4603c5a818bd01da69c00ad19

https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/digest#Converting_a_digest_to_a_hex_string


1

Solusi saya memperkenalkan fungsi statis untuk Objectobjek global .

(function() {
    var lastStorageId = 0;

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

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

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

Saya pikir ini lebih nyaman dengan fungsi memanipulasi objek lain dalam JavaScript


1
Objek dengan nilai internal yang sama akan hash ke hash yang berbeda, ini bukan apa yang hash (kode) lakukan.
Metalstorm

Dalam JavaScript (dan saya pikir di hampir setiap bahasa lain juga) dua objek yang dibuat dengan nilai internal yang sama masih objek yang berbeda, karena tipe data yang mendasari diwakili oleh instance objek baru masing-masing. jsfiddle.net/h4G9f
Johnny

4
Ya tapi itu bukan untuk apa kode hash, kode hash digunakan untuk memeriksa persamaan status suatu objek. Sama seperti hash, input yang sama masuk (nilai variabel) hash yang sama keluar. Apa yang Anda cari adalah UUID (yang disediakan oleh fungsi Anda).
Metalstorm

1
Kamu benar. Saya salah paham pertanyaannya. Sungguh buruk, bahwa jawaban yang diterima juga tidak memberikan solusi yang baik.
Johnny

Mengambil fungsi Anda juga, saya akan cenderung jadi lebih seperti ini: jsfiddle.net/xVSsd Hasil yang sama, lebih pendek (LoC + karakter), dan mungkin sedikit lebih cepat sedikit lebih cepat :)
Metalstorm

1

Saya akan mencoba sedikit lebih dalam daripada jawaban lainnya.

Walaupun JS memiliki dukungan hashing yang lebih baik, JS tidak akan secara ajaib melakukan hash semuanya dengan sempurna, dalam banyak kasus Anda harus mendefinisikan fungsi hash Anda sendiri. Misalnya Java memiliki dukungan hashing yang baik, tetapi Anda masih harus berpikir dan melakukan beberapa pekerjaan.

Satu masalah adalah dengan istilah hash / kode hash ... ada hashing kriptografi dan hashing non-kriptografi. Masalah lainnya, adalah Anda harus memahami mengapa hashing berguna dan cara kerjanya.

Ketika kita berbicara tentang hashing dalam JavaScript atau Java sebagian besar waktu kita berbicara tentang hashing non-kriptografi, biasanya tentang hashing untuk hashmap / hashtable (kecuali kita sedang mengerjakan otentikasi atau kata sandi, yang bisa Anda lakukan di sisi server menggunakan NodeJS. ..)

Itu tergantung pada data apa yang Anda miliki dan apa yang ingin Anda capai.

Data Anda memiliki keunikan "sederhana" yang alami:

  • Hash dari integer adalah ... integer, karena unik, beruntung Anda!
  • Hash suatu string ... itu tergantung pada string, jika string tersebut mewakili pengidentifikasi unik, Anda dapat menganggapnya sebagai hash (jadi tidak perlu hashing).
  • Apa pun yang secara tidak langsung merupakan bilangan bulat unik adalah kasus paling sederhana
  • Ini akan menghormati: kode hash sama jika objek sama

Data Anda memiliki keunikan "komposit" alami:

  • Misalnya dengan objek orang, Anda dapat menghitung hash menggunakan nama depan, nama belakang, tanggal lahir, ... lihat bagaimana Java melakukannya: Fungsi Hash yang Baik untuk String , atau menggunakan beberapa info ID lain yang murah dan cukup unik untuk penggunaan pengguna Anda

Anda tidak tahu akan menjadi apa data Anda:

  • Selamat mencoba ... Anda bisa membuat serial menjadi string dan meng-hash-nya dengan gaya Java, tapi itu mungkin mahal jika stringnya besar dan tidak akan menghindari tabrakan serta mengatakan hash dari integer (mandiri).

Tidak ada teknik hashing ajaib yang efisien untuk data yang tidak diketahui, dalam beberapa kasus itu cukup mudah, dalam kasus lain Anda mungkin harus berpikir dua kali. Jadi, bahkan jika JavaScript / ECMAScript menambahkan lebih banyak dukungan, tidak ada solusi bahasa ajaib untuk masalah ini.

Dalam latihan Anda membutuhkan dua hal: keunikan yang cukup, kecepatan yang cukup

Selain itu bagus untuk memiliki: "kode hash sama jika objek sama"


0

Jika Anda benar-benar ingin mengatur perilaku (saya menggunakan pengetahuan Java), maka Anda akan sulit sekali menemukan solusi dalam JavaScript. Sebagian besar pengembang akan merekomendasikan kunci unik untuk mewakili setiap objek, tetapi ini tidak seperti set, di mana Anda bisa mendapatkan dua objek yang identik masing-masing dengan kunci unik. Java API melakukan pekerjaan memeriksa nilai duplikat dengan membandingkan nilai kode hash, bukan kunci, dan karena tidak ada representasi nilai kode hash objek dalam JavaScript, menjadi hampir mustahil untuk melakukan hal yang sama. Bahkan perpustakaan Prototipe JS mengakui kekurangan ini, ketika dikatakan:

"Hash dapat dianggap sebagai array asosiatif, yang mengikat kunci unik ke nilai-nilai (yang tidak harus unik) ..."

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


0

Selain jawaban kelopak mata, berikut adalah fungsi yang mengembalikan ID unik yang dapat direproduksi untuk objek apa pun:

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);
}

Seperti yang Anda lihat itu menggunakan daftar untuk pencarian yang sangat tidak efisien, namun itu yang terbaik yang bisa saya temukan untuk saat ini.


0

Jika Anda ingin menggunakan objek sebagai kunci, Anda harus menimpa Metode toString mereka, seperti yang telah disebutkan di sini. Fungsi hash yang digunakan semuanya baik-baik saja, tetapi mereka hanya bekerja untuk objek yang sama bukan untuk objek yang sama.

Saya telah menulis perpustakaan kecil yang membuat hash dari objek, yang dapat Anda gunakan dengan mudah untuk tujuan ini. Objek bahkan dapat memiliki urutan berbeda, hash akan sama. Secara internal Anda dapat menggunakan berbagai jenis untuk hash Anda (djb2, md5, sha1, sha256, sha512, ripemd160).

Berikut adalah contoh kecil dari dokumentasi:

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[{}]);

Paket ini dapat digunakan di browser dan di Node-Js.

Repositori: https://bitbucket.org/tehrengruber/es-js-hash


0

Jika Anda ingin memiliki nilai unik dalam objek pencarian, Anda dapat melakukan sesuatu seperti ini:

Membuat objek pencarian

var lookup = {};

Menyiapkan fungsi kode hash

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;
}

Obyek

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

Himpunan

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

Tipe yang lain

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

Hasil akhir

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

Perhatikan bahwa getHashCodetidak mengembalikan nilai apa pun saat objek atau array kosong

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

Ini mirip dengan solusi @ijmacd getHashCodetetapi tidak memiliki JSONketergantungan.


Anda harus memiliki masalah dengan referensi melingkar dengan itu
tuxSlayer

@tuxSlayer Terima kasih telah memberi tahu saya. Anda dapat dengan mudah memperluas kode ini dengan kebutuhan Anda, tetapi saya harap idenya agak jelas :)
A1rPun

Ini akan menghasilkan kunci yang sangat panjang untuk objek besar yang dapat menekan memori dan kinerja cukup keras
Gershom

0

Saya menggabungkan jawaban dari kelopak mata dan KimKha.

Berikut ini adalah layanan angular dan mendukung angka, string, dan objek.

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;
};

Contoh penggunaan:

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')

Keluaran

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

Penjelasan

Seperti yang Anda lihat, inti dari layanan ini adalah fungsi hash yang dibuat oleh KimKha. Saya telah menambahkan tipe ke string sehingga struktur dari objek juga akan memengaruhi nilai hash akhir. Kunci hash untuk mencegah tumbukan array | objek benturan.

perbandingan objek kelopak mata digunakan untuk mencegah rekursi infinit dengan objek referensi diri.

Pemakaian

Saya membuat layanan ini sehingga saya bisa memiliki layanan kesalahan yang diakses dengan objek. Sehingga satu layanan dapat mendaftarkan kesalahan dengan objek yang diberikan dan yang lain dapat menentukan apakah ada kesalahan ditemukan.

yaitu

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"}'});

Ini akan mengembalikan:

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

Sementara

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

Ini akan kembali

[]

0

Cukup gunakan properti rahasia tersembunyi dengan defineProperty enumerable: false

Ini bekerja sangat cepat :

  • UniqueId baca pertama: 1.257.500 ops / s
  • Lainnya: 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
}
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.