2016-02-09 14 views
7

要約:オブジェクトをハッシュする高速な方法はJSON.stringifyですか?オブジェクト引数の効率的なメモ帳

詳細:JavaScript値をきれいに印刷できるRubyおよびJavaScriptライブラリ(NeatJSON)があります。私は最近、深くネストされたオブジェクトが、直列化されるオブジェクトとインデント量に基づいてメモを使用して、O(n!)のパフォーマンス(ネストレベルであるn)を引き起こした問題を修正しました。ルビー、the fix

は、本当に簡単だったことができますオブジェクトの一意のセットの配列によってインデックスハッシュ理由:内のJavaScriptで

build = ->(object,indent) do 
    memoizer[[object,indent]] ||= <all the rest of the code> 
end 

、しかし、私は、インデックス別のオブジェクトによって、オブジェクトをすることはできません(ユニークな方法)。私はオンラインで見つけるいくつかの記事のリードに続いて、私はメモ化のためのユニークなキーを作成する関数に引数のフルセットにJSON.stringifyを使用して、一般的にfix the problemに決める:

function memoize(f){ 
    var memo = {}; 
    var slice = Array.prototype.slice; 
    return function(){ 
    var args = slice.call(arguments); 
    var mkey = JSON.stringify(args); 
    if (!(mkey in memo)) memo[mkey] = f.apply(this,args); 
    return memo[mkey]; 
    } 
} 

function rawBuild(o,indent){ .. } 
var build = memoize(rawBuild); 

これは動作しますが、(a)はこれです(b)私がスマートにシリアル化しようとしているすべてのオブジェクトと値を(単純な)直列化を実行することは、非常に非効率的で(そして控えめで)思われます。多くの値を持つラージオブジェクトをシリアライズする行為は、文字列を格納し、オブジェクト全体にEVERY一意の値(葉の値だけではない)の結果を書式設定することになります。

値を一意に識別できる最新のJavaScriptトリックはありますか?たとえば、内部IDにアクセスする方法や、複雑なオブジェクトをO(1)時間を費やして値の識別子を見つけるユニークな整数に関連付ける方法はありますか?

+0

(http://stackoverflow.com/q/2020670/405017)。オブジェクトだけでなく、ほとんどの種類の値(文字列、論理値、数値、配列、オブジェクト)のユニークな表現を見つける必要があるため、かなり大変です。 – Phrogz

+0

オブジェクトの値または参照でメモしますか?言い換えれば、 'var a = {b:1}; var c = memoizedFn(a); a.b = 2; var d = memoizedFn(a); '2番目の呼び出しでmemoizedの値を使用する必要がありますか? –

+0

@TamasHegedus参照は完全に十分です。 – Phrogz

答えて

3

あなたのオブジェクトをアイデンティティ(コンテンツではなく)でメモしたい場合は、この目的のために設計されたWeakMapを使用します。これらは基本的な値では機能しませんので、そのような引数に対しては別の解決策が必要です。

+0

それはとても役に立ちます。そして、はい、この場合、同じツリー内の値を上下にクロールするだけなので、アイデンティティによるメモの作成で十分です。 – Phrogz

+0

私はプリミティブな値をサポートする必要があるので、['Map'](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map)がより適切であるように見えますWeakMapよりも。 – Phrogz

+0

@Phrogz:うん、ユースケースに依存します。メモ機能を保持している(または実行している)間にオブジェクトの一部が既にガベージコレクションされている場合、WeakMapはメモリ効率が向上します。 – Bergi

1

オブジェクトをメモする必要がある場合は、オブジェクトに一意のIDを割り当てるのが理にかなっています。

var gID = 0; 

function createNode() { 
    var obj = ... 
    obj.id = (++gID).toString(); 
} 

とあなたのmemoコレクションのキーとしてそれらobj.id年代を使用しています。

これは、最も速く、最も欲張りのない解決策です。

更新:

あなたはそのidプロパティは、あなたが(いくつかのユニークな名前を持つ)非可算標準ES5.1 Object.createProperty()を使用してプロパティを作成することができ、既存のプロパティ と衝突していないか、または使用したい場合はES6 symbols

私はおよそ Map、キー(だけではなくオブジェクト)として いかなる値の型を使用して可能になるのが分かった WeakMapのBergiの提案@使用
var gID = 0; 
var gUidSym = Symbol("uid"); 

function getUidOf(obj) { 
    return obj[gUidSym] 
     || (obj[gUidSym] = (++gID).toString()); 
} 
+0

2つの問題:1)私は作成されたオブジェクトを制御しません。これはJSONシリアライザで、人々が自分のオブジェクトを渡して処理します。 2)オブジェクトのプロパティをシリアル化しているので、ここで提案する 'id'プロパティはシリアル化に含まれます(おそらく既存のプロパティと競合する可能性があります)。 – Phrogz

+1

「更新:」の部分を確認してください。 –

+0

プロパティを作成する(文字列かシンボル名かにかかわらず)、密封されたオブジェクトでは機能しないという問題があります。 – Bergi

2

。シリアライズ時に

function memoizedBuild(){ 
    var memo = new Map; 
    return function(value,indent){ 
    var byIndent=memo.get(value); 
    if (!byIndent) memo.set(value,byIndent={}); 
    if (!byIndent[indent]) byIndent[indent] = rawBuild(value,indent); 
    return byIndent[indent]; 
    } 
} 

これはmemoization code I had been using比べて約4倍に高速であることが証明された:私は、キーを一意インデントに渡された値の組み合わせmemoizing化合物を必要なので、文字列を-Iは、階層的なメモ化構造を作成しました大きな270kBのJSONオブジェクト

私はrawBuildがfalsey値を返すことはありませんことを知っているので、上記のコードでは、私が唯一!byIndent[indent]を使用することができるよという注意(nullundefinedを、falseNaN0"")。より安全なコード行は次のようになります。

[JavaScriptのオブジェクトID]の(ではない、非常にDUP)と非常に類似し
if (!(indent in byIndent)) byIndent[indent] = rawBuild(value,indent);