มีฟังก์ชั่นแฮชโค้ดใน JavaScript หรือไม่?


150

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

set[obj] = true;

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


32
เหตุผลที่วัตถุทั้งหมด 'แฮช' เป็นค่าเดียวกันนั้นเป็นเพราะคุณยังไม่ได้แทนที่เมธอด toString ของมัน เนื่องจากต้องใช้คีย์เป็นสตริงเมธอด toString จะถูกเรียกโดยอัตโนมัติเพื่อรับคีย์ที่ถูกต้องดังนั้นวัตถุทั้งหมดของคุณจะแปลงเป็นสตริงเริ่มต้นเดียวกัน: "[object Object]"
Alanning

4
JSON.stringify(obj)หรือobj.toSource()อาจทำงานให้คุณขึ้นอยู่กับปัญหาและแพลตฟอร์มเป้าหมาย
AnnanFay

4
@Annan JSON.stringify (obj) แท้จริงเพียงแค่แปลงวัตถุ (ทั้งหมด) เป็นสตริง ดังนั้นคุณก็แค่คัดลอกวัตถุไปยังตัวมันเอง นี่คือไม่มีจุดหมายเสียพื้นที่และไม่เหมาะสม
Metalstorm

1
@ Metalstorm True ซึ่งเป็นสาเหตุที่มันขึ้นอยู่กับว่าปัญหาของคุณคืออะไร เมื่อฉันพบคำถามนี้ผ่านทาง google ทางออกสุดท้ายของฉันคือการเรียกไปที่แหล่งที่ () บนวัตถุ อีกวิธีหนึ่งก็คือการใช้แฮชแบบดั้งเดิมกับแหล่งที่มา
AnnanFay

@Annan toSourceไม่ทำงานใน Chrome btw
Pacerier

คำตอบ:


35

วัตถุ JavaScript สามารถใช้สตริงเป็นคีย์ได้เท่านั้น (สิ่งอื่นใดที่ถูกแปลงเป็นสตริง)

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

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

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

เห็นได้ชัดว่ามันเป็น verbose เล็ก ๆ น้อย ๆ แต่คุณสามารถเขียนวิธีการสองสามวิธีที่จัดการกับมันและรับและตั้งค่าทั้งหมดโดยเจตนา

แก้ไข:

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

นี่เป็นอีกประเด็นที่น่าสนใจ คุณสามารถกำหนดวิธีการ toString บนวัตถุที่คุณต้องการแฮชและที่สามารถจัดรูปแบบตัวระบุแฮชของพวกเขา


ตัวเลือกอื่นจะให้ค่าแต่ละวัตถุสุ่มเป็น hash - อาจเป็นตัวเลขสุ่ม + ติ๊กทั้งหมด - จากนั้นมีชุดฟังก์ชั่นเพื่อเพิ่ม / ลบวัตถุออกจากอาร์เรย์
Sugendran

4
สิ่งนี้จะล้มเหลวหากคุณเพิ่มวัตถุเดียวกันสองครั้ง มันจะคิดว่ามันแตกต่างกัน
Daniel X Moore

"สิ่งนี้จะล้มเหลวถ้าคุณเพิ่มวัตถุเดียวกันสองครั้งมันจะคิดว่ามันแตกต่างกัน" จุดดี. วิธีแก้ปัญหาอาจเป็น subclass Array สำหรับ ObjectReference โดยเชื่อมโยงการตรวจสอบซ้ำเข้าสู่ push () ตอนนี้ฉันไม่มีเวลาแก้ไขสำหรับโซลูชันนี้ แต่ฉันหวังว่าฉันจะจำในภายหลัง
ตน

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

35
จุด hashing ของวัตถุคืออะไรถ้าทุกครั้งที่คุณอ้างถึงพวกเขาที่คุณต้องการสแกนเชิงเส้นของอาร์เรย์?
Bordaigorl

57

หากคุณต้องการฟังก์ชั่น hashCode () เช่น Java ใน JavaScript นั่นคือของคุณ:

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

นั่นคือวิธีการใช้งานใน Java (ตัวดำเนินการระดับบิต)

โปรดทราบว่า hashCode อาจจะเป็นในเชิงบวกและเชิงลบและที่ปกติดูhashCode ให้ค่าลบ ดังนั้นคุณสามารถพิจารณาใช้Math.abs()พร้อมกับฟังก์ชั่นนี้


5
สิ่งนี้สร้าง -hash, not perfect
qodeninja

2
@KimKha charเป็นคำที่สงวนไว้ใน JS และอาจทำให้เกิดปัญหาบางอย่าง ชื่ออื่นจะดีกว่า
szeryf

16
@qodeninja พูดว่าใคร นี่เป็นครั้งแรกที่ฉันได้ยินการเรียกร้องดังกล่าว คุณสามารถลิงค์ไปยังบางแหล่งได้ไหม? โดยปกติจะมีการคำนวณแฮชโดยใช้การคำนวณทางคณิตศาสตร์จำนวนเต็มและการดำเนินการบิตดังนั้นการได้ผลลัพธ์ที่เป็นค่าบวกหรือลบนั้นเป็นสิ่งที่คาดหวัง
szeryf

7
พิถีพิถัน แต่ ... "ถ้า (this.length == 0) ส่งคืนแฮช" ซ้ำซ้อน :) และโดยส่วนตัวแล้วจะเปลี่ยน "ตัวละคร" เป็น "รหัส"
Metalstorm

10
@qodeninja และ @szeryf: คุณแค่ต้องระวังว่าคุณใช้มันอย่างไร ตัวอย่างเช่นฉันพยายามทำpickOne["helloo".hashCode() % 20]อาร์เรย์ที่pickOneมี 20 องค์ประกอบ ฉันได้รับundefinedเนื่องจากรหัสแฮชเป็นค่าลบดังนั้นนี่คือตัวอย่างที่มีคน (ฉัน) ที่คิดว่ารหัสแฮชบวกโดยปริยาย
จิม Pivarski

31

วิธีที่ง่ายที่สุดในการทำเช่นนี้คือการให้toStringวิธีการเฉพาะของวัตถุแต่ละอย่าง:

(function() {
    var id = 0;

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

ฉันมีปัญหาเดียวกันและสิ่งนี้แก้ไขได้อย่างสมบูรณ์แบบสำหรับฉันด้วยความยุ่งยากน้อยที่สุดและง่ายขึ้นมากที่นำรูปแบบ Java ไขมันHashtableและการเพิ่มequals()และhashCode()เรียนวัตถุของคุณอีกครั้ง เพียงตรวจสอบให้แน่ใจว่าคุณไม่ได้ติดสตริง '<#MyObject: 12> ไว้ในแฮชของคุณมิเช่นนั้นจะเป็นการลบรายการสำหรับวัตถุที่กำลังออกด้วย id นั้น

ตอนนี้แฮชทั้งหมดของฉันเย็น ฉันก็เป็นเพียงแค่โพสต์รายการบล็อกไม่กี่วันที่ผ่านมาเกี่ยวกับหัวข้อนี้แน่นอน


28
แต่นี่พลาดจุดทั้งหมด Java มีequals()และhashCode()เพื่อให้สองวัตถุที่เทียบเท่ามีค่าแฮชเดียวกัน การใช้วิธีการด้านบนหมายความว่าทุกอินสแตนซ์ของMyObjectจะมีสตริงที่ไม่ซ้ำกันซึ่งหมายความว่าคุณจะต้องทำการอ้างอิงกับวัตถุนั้นเพื่อดึงค่าที่ถูกต้องจากแผนที่ การมีคีย์นั้นไม่มีความหมายเนื่องจากไม่มีอะไรเกี่ยวข้องกับเอกลักษณ์ของวัตถุ toString()จะต้องใช้ฟังก์ชั่นที่มีประโยชน์สำหรับประเภทของวัตถุเฉพาะที่คุณใช้เป็นกุญแจ
sethro

@sethro คุณสามารถใช้toStringสำหรับวัตถุที่มันแมปโดยตรงกับความสัมพันธ์ที่เท่าเทียมกันเพื่อให้วัตถุทั้งสองสร้างสตริงเดียวกัน iff พวกเขาจะถือว่า "เท่าเทียมกัน"
Daniel X Moore

3
ขวาและที่เป็นเพียงวิธีที่ถูกต้องในการใช้toString()เพื่อให้คุณสามารถใช้เป็นObject Setฉันคิดว่าฉันเข้าใจผิดคำตอบของคุณว่าพยายามเสนอวิธีแก้ปัญหาทั่วไปเพื่อหลีกเลี่ยงการเขียนtoString()เทียบเท่าequals()หรือhashCode()เป็นกรณี ๆ ไป
sethro

3
Dowvoted นี่ไม่ใช่สิ่งที่ hashcode คือดูคำตอบของฉันไปที่: stackoverflow.com/a/14953738/524126และการใช้งานจริงของ hashcode: stackoverflow.com/a/15868654/524126
Metalstorm

5
@ Metalstorm คำถามไม่ได้ถามเกี่ยวกับแฮชรหัส "จริง" แต่จะใช้วัตถุเป็นชุดใน JavaScript ได้อย่างไร
Daniel X Moore

20

สิ่งที่คุณอธิบายครอบคลุมโดย Harmony WeakMapsซึ่งเป็นส่วนหนึ่งของข้อกำหนดECMAScript 6 (JavaScript เวอร์ชันถัดไป) นั่นคือ: ชุดที่คีย์สามารถเป็นอะไรก็ได้ (รวมถึงไม่ได้กำหนด) และไม่สามารถนับได้

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

จาก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 มีให้บริการใน Firefox, Chrome และ Edge ปัจจุบัน พวกเขายังได้รับการสนับสนุนในโหนด v7 และใน v6 กับ--harmony-weak-mapsธง


1
ความแตกต่างระหว่างสิ่งเหล่านี้และMapคืออะไร?
smac89

@ smac89 WeakMap มีข้อ จำกัด : 1) ใช้วัตถุเป็นคีย์เท่านั้น 2) ไม่มีคุณสมบัติขนาด 3) ไม่มีตัววนซ้ำหรือสำหรับแต่ละวิธี 4) ไม่มีวิธีที่ชัดเจน คีย์คือวัตถุ - ดังนั้นเมื่อวัตถุจะถูกลบออกจากหน่วยความจำ - ข้อมูลจาก WeakMap ที่เชื่อมต่อกับวัตถุนี้จะถูกลบด้วย มันมีประโยชน์มากเมื่อเราต้องการเก็บข้อมูลซึ่งควรมีอยู่ในขณะที่วัตถุนั้นมีอยู่เท่านั้น ดังนั้น WeakMap มีเพียงวิธีการ: ตั้งค่าลบสำหรับเขียนและรับมีเพื่ออ่าน
Ekaterina Tokareva

สิ่งนี้ทำงานไม่ถูกต้อง ... var m = new Map();m.set({},"abc"); console.log(m.get({}) //=>undefinedใช้งานได้เฉพาะเมื่อคุณมีตัวแปรเดียวกับที่คุณอ้างอิงในคำสั่ง set EGvar m = new Map();a={};m.set(a,"abc"); console.log(m.get(a) //=>undefined
Sancarn

1
@Sancarn มันไม่จำเป็นต้องเป็นตัวแปรเดียวกัน แต่พวกมันต้องชี้ไปที่วัตถุเดียวกัน ในตัวอย่างแรกของคุณคุณมีวัตถุสองชนิดที่แตกต่างกันพวกมันมีลักษณะเหมือนกัน แต่มีที่อยู่ต่างกัน
Svish

1
@ เห็นเป็นจุดที่ดี! แต่ผมรู้ว่ามันตอนนี้ผมอาจจะไม่ได้ทำกลับมาแล้ว :)
Sancarn

19

โซลูชันที่ฉันเลือกนั้นคล้ายคลึงกับของ Daniel แต่แทนที่จะใช้โรงงานวัตถุและแทนที่ toString ฉันเพิ่มแฮชเข้ากับวัตถุอย่างชัดเจนเมื่อได้รับการร้องขอครั้งแรกผ่านฟังก์ชัน getHashCode ยุ่งเล็กน้อย แต่ดีกว่าสำหรับความต้องการของฉัน :)

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

7
หากคุณต้องการที่จะไปทางนี้มันมากดีกว่าที่จะตั้ง hashCode ผ่านObject.definePropertyกับenumerableชุดfalseดังนั้นคุณจะไม่ผิดพลาดใด ๆfor .. inลูป
เซบาสเตียนโนวัก

14

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

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

ฉันได้รวบรวมโมดูล JavaScript ขนาดเล็กไว้ครู่หนึ่งเพื่อสร้างแฮชโค้ดสำหรับสตริงวัตถุอาร์เรย์ ฯลฯ (ฉันเพิ่งมุ่งมั่นกับGitHub :))

การใช้งาน:

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

GC ของ javascript ไม่สำลักกับการอ้างอิงแบบวงกลมด้วยหรือไม่
Clayton Rabenda

@ Ryan ยาวฉันอาจไปเท่าที่บอกว่าถ้าคุณมีการอ้างอิงแบบวงกลมแล้วคุณจำเป็นต้อง refactor รหัสของคุณ;)
Metalstorm

11
@ Metalstorm "จากนั้นคุณต้อง refactor รหัสของคุณ" คุณล้อเล่น? ผู้ปกครององค์ประกอบ DOM และคู่เด็กทุกคนถือว่าเป็นการอ้างอิงแบบวงกลม
Chris Middleton

8
มันเป็นงานที่ไม่ดี hashing วัตถุที่มีคุณสมบัติเป็นตัวเลขคืนค่าเดียวกันในหลาย ๆ กรณีเช่นvar hash1 = Hashcode.value({ a: 1, b: 2 }); var hash2 = Hashcode.value({ a: 2, b: 1 }); console.log(hash1, hash2);จะเข้าสู่ระบบ2867874173 2867874173
Julien Bérubé

9

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

myObject[myProperty] = ...;

เป็นเช่นเดียวกับ

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

สิ่งนี้จำเป็นเช่นเดียวกับใน JavaScript

myObject["someProperty"]

เป็นเช่นเดียวกับ

myObject.someProperty

และใช่มันทำให้ฉันเศร้าเช่นกัน :-(


9

ใน ECMAScript 6 ตอนนี้มีSetวิธีการทำงานที่คุณต้องการ: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set

มีให้บริการแล้วใน Chrome, FF และ IE11 ล่าสุด


1
นี่ควรเป็นคำตอบที่ดีที่สุดในปี 2016 ถ้าคุณใช้ Babel คุณสามารถใช้ Set ตามข้อมูลจำเพาะ ES6 และจะถูกเติมโดยอัตโนมัติในเอาต์พุต ES5 babeljs.io/docs/learn-es2015/#map-set-weak-map-weak-set
atroberts20

5

การอ้างอิง: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol

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

var obj = {};

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

2
ยกเว้นว่าไม่มีวิธีในการสร้างสัญลักษณ์ใหม่ให้ x = Symbol ('a'); ให้ y = สัญลักษณ์ ('a'); console.log (x === y); // ส่งกลับค่าเท็จดังนั้น Symbol ไม่ทำงานเหมือนแฮช
Richard Collette

3

นี่เป็นวิธีง่ายๆของฉันที่คืนค่าจำนวนเต็มเฉพาะ

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
ความซับซ้อนของสิ่งนี้จะทำลายความคิดทั้งหมดที่อยู่เบื้องหลัง hashCode ()
tuxSlayer

ฉันไม่พบว่ามันซับซ้อนเกินความจำเป็น ฉันอยากรู้อยากเห็น: ทำไมระยะเปลี่ยน? การยกเว้นจะมีผลตอบแทนเป็นอย่างอื่นใน charCodeAt ใช่ไหม?
Greg Pettit

น่าเสียดายเพราะhashcode({a:1, b:2}) === hashcode({a:2, b:1})ความขัดแย้งอื่น ๆ อีกมากมาย
maaartinus

3

จากชื่อเรื่องเราสามารถสร้าง hash ที่แข็งแกร่งด้วย js สามารถใช้เพื่อสร้าง hash ที่ไม่ซ้ำกันจากวัตถุอาร์เรย์ของ params สตริงหรืออะไรก็ตาม

ต่อมาสำหรับการทำดัชนีสิ่งนี้จะหลีกเลี่ยงข้อผิดพลาดที่อาจเกิดขึ้นในขณะที่อนุญาตให้ดึงดัชนีจาก params (หลีกเลี่ยงการค้นหา / วนลูปวัตถุ ฯลฯ ):

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

ผลลัพธ์ข้างต้นในเบราว์เซอร์ของฉันก็ควรจะมีค่าเท่ากันสำหรับคุณเช่นกัน ( จริงเหรอ? ):

bf1cf3fe6975fe382ab392ec1dd42009380614be03d489f23601c11413cfca2b
93a23971a914e5eacbf0a8d25154cda309c3c1c72fbb9914d47c60f3cb681588
d2f209e194045604a3b15bdfd7502898a0e848e4603c5a818bd01da69c00ad19

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


1

โซลูชันของฉันแนะนำฟังก์ชันคงที่สำหรับObjectวัตถุทั้งหมด

(function() {
    var lastStorageId = 0;

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

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

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

ฉันคิดว่ามันสะดวกกว่ากับอ็อบเจกต์อื่นที่ใช้ฟังก์ชั่นใน JavaScript


1
วัตถุที่มีค่าภายในเดียวกันจะแฮชไปยังแฮชต่างกันนี่ไม่ใช่สิ่งที่แฮช (รหัส) ทำ
Metalstorm

ใน JavaScript (และฉันคิดว่าในเกือบทุกภาษาอื่น ๆ ด้วย) สองวัตถุที่สร้างขึ้นด้วยค่าภายในเดียวกันยังคงเป็นวัตถุที่แตกต่างกันเนื่องจากประเภทข้อมูลที่อยู่ภายใต้การแสดงโดยวัตถุใหม่แต่ละตัวอย่าง jsfiddle.net/h4G9f
Johnny

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

1
คุณถูก. ฉันคิดถึงคำถาม จริง ๆ แล้วแย่ว่าคำตอบที่ยอมรับไม่ได้ให้ทางออกที่ดีเช่นกัน
Johnny

ใช้งานฟังก์ชั่นของคุณฉันก็อยากจะทำแบบนี้มากขึ้น: jsfiddle.net/xVSsdผลลัพธ์เดียวกันสั้นลง (LoC + chars) และอาจเร็วขึ้นเล็กน้อย :)
Metalstorm

1

ฉันจะพยายามไปให้ลึกกว่าคำตอบอื่นเล็กน้อย

แม้ว่า JS จะมีการสนับสนุนการแฮชที่ดีกว่ามันก็จะไม่แฮชทุกอย่างอย่างสมบูรณ์แบบในหลาย ๆ กรณีคุณจะต้องกำหนดฟังก์ชั่นแฮชของคุณเอง ตัวอย่างเช่น Java มีการสนับสนุนการแปลงแป้นพิมพ์ที่ดี แต่คุณยังต้องคิดและทำงานบางอย่าง

ปัญหาหนึ่งคือกับคำว่าแฮช / แฮชโค้ด ... มีการเข้ารหัสลับการเข้ารหัสลับและการเข้ารหัสลับที่ไม่ใช่การเข้ารหัสลับ อีกปัญหาหนึ่งคือคุณต้องเข้าใจว่าทำไมการแฮชจึงมีประโยชน์และวิธีการทำงาน

เมื่อเราพูดถึง hashing ใน JavaScript หรือ Java ส่วนใหญ่เราจะพูดถึง hashing ที่ไม่ใช่การเข้ารหัสโดยปกติเกี่ยวกับ hashing สำหรับ hashmap / hashtable (เว้นแต่ว่าเรากำลังทำงานเกี่ยวกับการรับรองความถูกต้องหรือรหัสผ่านซึ่งคุณสามารถใช้ฝั่งเซิร์ฟเวอร์โดยใช้ NodeJS .. )

ขึ้นอยู่กับว่าคุณมีข้อมูลใดและต้องการบรรลุอะไร

ข้อมูลของคุณมีเอกลักษณ์ "เรียบง่าย" เป็นธรรมชาติ:

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

ข้อมูลของคุณมีเอกลักษณ์ "คอมโพสิต" ตามธรรมชาติ:

  • ตัวอย่างเช่นกับวัตถุบุคคลคุณอาจคำนวณแฮชโดยใช้ชื่อนามสกุลวันเกิด ... ดูว่า Java ทำอย่างไร: ฟังก์ชั่นแฮชที่ดีสำหรับสตริงหรือใช้ข้อมูล ID อื่น ๆ ที่ราคาถูกและไม่ซ้ำกันมากพอสำหรับ usecase ของคุณ

คุณไม่รู้ว่าข้อมูลของคุณจะเป็นอย่างไร:

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

ไม่มีเทคนิคการแฮชที่มีประสิทธิภาพอย่างน่าอัศจรรย์สำหรับข้อมูลที่ไม่รู้จักในบางกรณีมันค่อนข้างง่ายในบางกรณีคุณอาจต้องคิดสองครั้ง ดังนั้นแม้ว่า JavaScript / ECMAScript จะเพิ่มการสนับสนุนมากขึ้นก็ไม่มีวิธีแก้ปัญหาภาษาเวทมนต์สำหรับปัญหานี้

ในทางปฏิบัติคุณต้องการสองสิ่ง: ความเป็นเอกลักษณ์เพียงพอความเร็วที่เพียงพอ

นอกเหนือจากนั้นมันยอดเยี่ยมที่จะมี: "hashcode เท่ากันถ้าวัตถุเท่ากัน"


0

หากคุณต้องการพฤติกรรมที่ตั้งค่าอย่างแท้จริง (ฉันจะรู้ด้วย Java) คุณจะยากที่จะหาวิธีแก้ไขใน JavaScript นักพัฒนาส่วนใหญ่จะแนะนำให้ใช้คีย์ที่ไม่ซ้ำกันในการแสดงวัตถุแต่ละอัน แต่นี่ไม่เหมือนชุดซึ่งคุณสามารถรับวัตถุที่เหมือนกันสองอันแต่ละอันด้วยคีย์ที่ไม่ซ้ำกัน Java API ทำหน้าที่ตรวจสอบค่าที่ซ้ำกันโดยการเปรียบเทียบค่ารหัสแฮชไม่ใช่คีย์และเนื่องจากไม่มีการแสดงค่ารหัสแฮชของวัตถุใน JavaScript จึงแทบเป็นไปไม่ได้ที่จะทำแบบเดียวกัน แม้แต่ไลบรารี Prototype JS ก็ยอมรับข้อบกพร่องนี้เมื่อมันกล่าวว่า:

"แฮชสามารถคิดได้ว่าเป็นอาเรย์แบบเชื่อมโยงเชื่อมโยงคีย์เฉพาะกับค่า (ซึ่งไม่จำเป็นต้องซ้ำกัน) ... "

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


0

นอกเหนือจากคำตอบของเปลือกตานี่คือฟังก์ชั่นที่ส่งกลับ ID ที่ทำซ้ำได้สำหรับวัตถุใด ๆ :

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

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


0

หากคุณต้องการใช้วัตถุเป็นกุญแจคุณต้องเขียนทับวิธี toString ตามที่บางคนกล่าวถึงแล้ว ฟังก์ชั่นแฮชที่ใช้ทั้งหมดนั้นดี แต่มันใช้ได้กับวัตถุเดียวกันเท่านั้นไม่ใช่วัตถุที่เท่ากัน

ฉันเขียนไลบรารีขนาดเล็กที่สร้างแฮชจากวัตถุซึ่งคุณสามารถใช้เพื่อวัตถุประสงค์นี้ได้อย่างง่ายดาย วัตถุสามารถมีลำดับที่แตกต่างกันได้แฮชจะเหมือนกัน ภายในคุณสามารถใช้ประเภทต่างๆสำหรับแฮช (djb2, md5, sha1, sha256, sha512, ripemd160)

นี่คือตัวอย่างเล็ก ๆ จากเอกสาร:

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

แพคเกจสามารถใช้ได้ทั้งในเบราว์เซอร์และใน Node-Js

พื้นที่เก็บข้อมูล: https://bitbucket.org/tehrengruber/es-js-hash


0

หากคุณต้องการมีค่าที่ไม่ซ้ำกันในวัตถุค้นหาคุณสามารถทำสิ่งนี้:

การสร้างวัตถุค้นหา

var lookup = {};

การตั้งค่าฟังก์ชัน hashcode

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

วัตถุ

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

แถว

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

ประเภทอื่น ๆ

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

ผลสุดท้าย

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

โปรดทราบว่าgetHashCodeจะไม่ส่งคืนค่าใด ๆ เมื่อวัตถุหรืออาร์เรย์ว่างเปล่า

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

สิ่งนี้คล้ายกับโซลูชัน @ijmacd เท่านั้นที่getHashCodeไม่มีการJSONพึ่งพา


คุณต้องมีปัญหากับการอ้างอิงแบบวงกลมกับที่
tuxSlayer

@tuxSlayer ขอบคุณที่แจ้งให้เราทราบ คุณสามารถขยายโค้ดนี้ได้อย่างง่ายดายตามความต้องการของคุณ แต่ฉันหวังว่าความคิดนั้นค่อนข้างชัดเจน :)
A1rPun

สิ่งนี้จะสร้างกุญแจที่มีความยาวมากสำหรับวัตถุขนาดใหญ่ซึ่งอาจโจมตีหน่วยความจำและประสิทธิภาพค่อนข้างยาก
Gershom

0

ฉันรวมคำตอบจากเปลือกตาและคิมคา

ต่อไปนี้เป็นบริการ angularjs และรองรับตัวเลขสตริงและวัตถุ

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

ตัวอย่างการใช้งาน:

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

เอาท์พุต

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

คำอธิบาย

อย่างที่คุณเห็นหัวใจของการบริการคือฟังก์ชั่นแฮชที่สร้างขึ้นโดย KimKha.I ได้เพิ่มประเภทให้กับสตริงเพื่อที่โครงสร้างของวัตถุจะส่งผลกระทบต่อค่าแฮชสุดท้ายปุ่มถูกแฮชเพื่อป้องกันการชนกันของอาร์เรย์

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

การใช้

ฉันสร้างบริการนี้เพื่อให้ฉันสามารถมีบริการข้อผิดพลาดที่เข้าถึงได้ด้วยวัตถุ เพื่อให้บริการหนึ่งสามารถลงทะเบียนข้อผิดพลาดกับวัตถุที่กำหนดและบริการอื่นสามารถตรวจสอบว่าพบข้อผิดพลาดใด ๆ

กล่าวคือ

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

สิ่งนี้จะกลับมา:

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

ในขณะที่

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

สิ่งนี้จะกลับมา

[]

0

เพียงใช้ทรัพย์สินลับที่ซ่อนอยู่กับ defineProperty enumerable: false

มันทำงานได้เร็วมาก :

  • ครั้งแรกที่อ่านรหัสที่ไม่ซ้ำกัน: 1,257,500 ops / s
  • คนอื่น ๆ ทั้งหมด: 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
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.