2014-07-23 18 views
3

我需要從JavaScript對象中創建校驗和。
不幸的是,由於JavaScript的對象排序,似乎沒有一種簡單的方法可以實現這一點。例如,拿這些對象:如何從JavaScript對象中創建校驗和?

var obj1 = {type:"cake",quantity:0} 
    , obj2 = {quantity:0,type:"cake"}; 

我認爲這些對象在數據上是相同的,並且希望它們的校驗和相同。只要數據中的數據是相同的,我真的不關心對象的順序。
唉,JSON.stringify其實並不相等;作爲一個對象的校驗和的唯一方法是通過它的字符串表示形式和JSON.stringify -ed表達式不相等,我的校驗和將不相等!
一個解決方案我想出了是基於預定義模式來重新創建對象,像這樣:

var schema = ["type","quantity"]; 
function sortify(obj,schema){ 
    var n={}; 
    for(i in schema) 
    n[schema[i]]=obj[schema[i]]; 
    return n 
} 

運行JSON.stringify(sortify(obj1,schema))==JSON.stringify(sortify(obj2,schema))將返回true ......但在創建新的對象和周圍洗牌的價格數據。

我的另一個解決方案是將JSON.stringify方法替換爲從預定義模式中選取鍵並對其值進行串處理,然後將它們連接在一起的方法。 該功能可以讀取:

function smarterStringify(obj,schema){ 
    var s=""; 
    for(i in schema) 
    s+=JSON.stringify(obj[schema[i]]); 
    return s 
} 

忽略了一個事實,這個方法不返回正確的JSON(它足夠接近的什麼,我試圖做一個例子),它是在第一個大規模的改進在速度上(至少在我的Chrome操作系統瀏覽器中,你可以在這裏檢查它:http://jsperf.com/sort-then-json-stringify-vs-smarter-stringify),當然它使兩個對象字符串表示相等!

但是,我只是想知道我是否錯過了一些東西,並且有一種類似這樣的內置方法,但沒有a)將JavaScript GC驅動成病態的情況,或者b)做得太多字符串連接。 我寧願不要那樣做。

+0

您不應該使用'for ... in'循環遍歷JavaScript中的數組。對索引變量使用簡單的'for'循環或使用'.forEach()'。 – Pointy

+0

對象中的所有屬性值是簡單值還是可以是對象或數組(例如嵌套結構)? – jfriend00

+0

@ jfriend00我打算在裏面有另一個對象,但這不是必需的 – striking

回答

2

您可以將密鑰收集到Object.keys()的數組中,對該數組進行排序,然後按照已知的可預測順序對鍵/值進行校驗和。我不知道有什麼方法可以一次使用JSON.stringify()與所有排序的鍵,但是您必須執行自己的校驗和。

我不知道任何像這樣的內置方法。對象鍵不保證以任何特定的順序,因此依靠它是不安全的。


如果你沒有嵌套的對象或數組的屬性值,那麼你可以做這樣的事情:

// creates an array of alternating property name, property value 
// with properties in sorted order 
// then stringify's that array 
function stringifyPropsInOrder(obj) { 
    var keys = Object.keys(obj).sort(); 
    var output = [], prop; 
    for (var i = 0; i < keys.length; i++) { 
     prop = keys[i]; 
     output.push(prop); 
     output.push(obj[prop]); 
    } 
    return JSON.stringify(output); 
} 

function compareObjects(a, b) { 
    return stringifyPropsInOrder(a) === stringifyPropsInOrder(b); 
} 

如果你想更快的性能,你不必字符串化(這只是爲了保存代碼而完成的)。你可以返回扁平的output數組並直接比較數組。


如果你能有嵌入對象的屬性值,然後一些更多的工作需要做,以recusively擴大到那些屬性/值相同的扁平陣列。

+0

爲什麼downvote? – jfriend00

4

你也可以將它的對象比較到位的函數:

function compareObjects(a, b) { 
    var akeys = Object.keys(a); 
    var bkeys = Object.keys(b); 
    var len = akeys.length; 
    if (len != bkeys.length) return false; 
    for (var i = 0; i < len; i++) { 
    if (a[akeys[i]] !== b[akeys[i]]) return false; 
    } 
    return true; 
} 

這是假定他們是通過了對象,他們是簡單的平面物體。可以添加邏輯來檢查這些假設,並遞歸檢查子對象是否相等。

+0

這是假設你用Object.keys()從兩個對象中獲得的鍵都是完全相同的順序,不能保證是特別的東西。 – jfriend00

+2

不,因爲它只使用'akeys'。 – Razem

+0

您假設'Object.keys()'在返回數組中完全相同的位置提供了相同的命名屬性。但是,由於Object.keys()以與for/in相同的順序傳遞屬性,所以假設屬性按規定的順序進行迭代,而規範並不保證這種順序。 – jfriend00

1

三年後...

我碰到這個問題就來了,我想我的哈希JSON對象爲我的HTTP響應創建Etag秒。所以,我最後寫我自己的節點,jsum,這可以歸結爲一個簡單的串行解決方案:

** 
* Stringifies a JSON object (not any randon JS object). 
* 
* It should be noted that JS objects can have members of 
* specific type (e.g. function), that are not supported 
* by JSON. 
* 
* @param {Object} obj JSON object 
* @returns {String} stringified JSON object. 
*/ 
function serialize (obj) { 
    if (Array.isArray(obj)) { 
    return JSON.stringify(obj.map(i => serialize(i))) 
    } else if (typeof obj === 'object' && obj !== null) { 
    return Object.keys(obj) 
     .sort() 
     .map(k => `${k}:${serialize(obj[k])}`) 
     .join('|') 
    } 

    return obj 
} 

然後,您可以採取的結果,並使用通用算法散列它(如SHA256),或使用的簡便方法digest來自jsum包。


請注意許可here

+0

簡短和重點。也許你可以把它和decycle結合起來(https://github.com/douglascrockford/JSON-js),以便處理循環結構 – pkExec