2016-10-14 7 views
1

を挿入アップデートJavaScriptオブジェクト、ネストされたデータ - のiは、以下のデータを持っていると言うだけではない場合、更新

scoressitesscoresidをもとに独自の要素を持つ配列を含み、 sitessidに基づいていることに注意してください
var obj = { 
    test: 'somedata', 
    scores: [ 
    { 
     "points":99, 
     "id":"x12" 
    }, 
    { 
     "points":21, 
     "id":"x13" 
    } 
    ], 
    sites: [ 
    { 
     "exercises":21, 
     "sid":"s12" 
    }, 
    { 
     "exercises":23, 
     "sid":"s11" 
    } 
    ], 
    history: { 
    key: 'value', 
    commits: [ 
     { 
     id: 1, 
     value: 'thank you' 
     } 
     ] 
    } 
} 

。私は、次の魔法行う関数たい:

//will **update** obj.test to 'newdata' and return {test:'newdata'} 
magicUpdate(obj, {test:'newdata'}) 

//will **insert** obj.newkey with with value 'value' and return {newkey: 'value'} 
magicUpdate(obj, {newkey: 'value'}) 

//will do nothing and return {} 
magicUpdate(obj, {scores: []}) 

//will **update** scores[0] and return {scores:[{points:3, id: "x12"}]}, as id "x12" is already in the array at index 0 
magicUpdate(obj, {scores:[{points:3, id: "x12"}]) 

//will **insert** {points:3, id: "x14"} into obj.scores and return {scores:[{points:3, id: "x14"}]} 
magicUpdate(obj, {scores:[{points:3, id: "x14"}]}) 

//will **update** sites[0] and return {sites:[{exercises:22, sid: "s12"}]}, as id "s12" is already in the array at index 0 
magicUpdate(obj, {sites:[{exercises:22, sid: "s12"}]) 

//will **insert** {exercises:10, sid: "s14"} into obj.sites and return {sites:[{exercises:10, sid: "s14"}]} 
magicUpdate(obj, {sites:[{exercises:10, sid: "s14"}]}) 

//and also be recursive ... 
//will **update** obj.history.commits[0] 
magicUpdate(obj, {'history.commits': [{id:1, value: 'changed'}]}); 

を私は再帰をやって.updateを見てきましたが、一つは自動的に決定されなければならないpathを通過している場合にのみ。その後、内部で_.baseMergeを使用する.mergeがあり、私は機能のシグネチャを理解していませんが、私が必要とするものに本当に近い状態になります。

_.merge(
{scores:[{id: 12, points:10}, {id: 13, points:10}]}, 
{scores:[{id: 14, points:10}, {id: 15, points:10}]} 
) 
// returns {scores:[{id: 14, points:10}, {id: 15, points:10}]} not the fully merged array 

誰かがいい方向に向けることができますか、またはロダッシュと同様のことを達成できますか?

+0

これまで何を試みましたか? –

+0

私は再帰が必要であることに気がついたときに良くなかった自分自身の関数を書こうとしました。私は上記のロダッシュの機能を見つけ、誰かが以前に同じ問題を抱えている可能性があると考えました。 – niklas

+0

オブジェクトとネストされたオブジェクトが完全に連携していることに気付きました。たぶん私は自分のデータ構造を '{id:s1、points:2}、...]'から 'score:{s1:{points:2}、...}'に変換しようとします。 – niklas

答えて

0

私は、再帰的な、非常にパフォーマンスなソリューションを書きました。 See this fiddle.

function mergeRecursive(obj1, obj2) { 
    if (obj1.constructor == Array) { 
    for (var i = 0; i < obj1.length; i++) { 
     if (obj1[i].id == obj2.id) { 
     obj1[i] = obj2; 
     return obj1; 
     } 
    } 
    obj1.push(obj2); 
    return obj1; 
    } 
    for (var p in obj2) { 
    // Property in destination object set; update its value. 
    if (obj2[p].constructor == Array) { 
     obj2[p].forEach(function(arrayElement) { 
     obj1[p] = MergeRecursive(obj1[p], arrayElement); 
     }); 
    } else if (obj2[p].constructor == Object) { 
     obj1[p] = MergeRecursive(obj1[p], obj2[p]); 
    } else { 
     obj1[p] = obj2[p]; 
    } 
    } 
    return obj1; 
} 
1

あなたの記事で言及しているmagicUpdate機能は、実際にはロダッシュ機能を使用して達成することができました。あなたは、コードを見て気づいたかもしれませんが

// src will be mutated. For simplicity's sake, obj is an object with only one property that represents the changes to make to src 
function magicUpdate(src, obj) { 
    var key = _.first(_.keys(obj)), 
    value = _.get(obj, key), 
    srcValue = _.get(src, key), 
    comparator = function(a, b) { 
     var idKey = _.isUndefined(a.id) ? 'sid' : 'id'; 
     return a[idKey] === b[idKey]; 
    } 

    if (_.isArray(srcValue)) { 
    value = _.unionWith(value, srcValue, comparator); 
    } 

    return _.set(src, key, value); 
} 

、:私はそれが他のいくつかを使用して達成されている可能性が確信しているものの、この実装では

、私はほとんど_ .get_ .set_ .unionWithを使用しました戻り値の型は、あなたが求めているものではなく、変更されたオブジェクトです。私はあなたが返品価値として望んでいたことを本当に確信していませんでした。

とにかく、Lodashにはオブジェクトの差分機能が組み込まれていないので、古いオブジェクトと変更されたオブジェクトの違いを欲しがるような場合に備えて開発する必要があります_ .clone最初にオブジェクトをコピーして比較できるようにする)。

私が提示する関数のアイデアは、objのキーを取得しようとしています(これは、srcで変更したいキーです)。それが存在し、配列であるかどうかを確認してください。その場合は、idobjにあるsrcのものを更新して、2つの配列を追加するだけです。 sites,scoresおよびhistoryidおよびsidであるという事実のために、私は_.unionWith機能のコンパレータにいくつかのロジックを追加する必要がありました。

keyが存在しないか、配列でない場合は、srcに設定します。

ここでは、再生する場合のためにfiddleがあります。それが役に立てば幸い。

UPDATE

私の最初のソリューションを一度に更新つの性質のために意図されていました。しかし、同時に複数のものを更新することは可能です。

一度に1つのプロパティを更新し、オブジェクトを更新してオブジェクトを繰り返し処理するのが簡単な解決策です。

function updateProperty(src, obj) { 
    var key = _.first(_.keys(obj)), 
    value = _.get(obj, key), 
    srcValue = _.get(src, key), 
    comparator = function(a, b) { 
     var idKey = _.isUndefined(a.id) ? 'sid' : 'id'; 
     return a[idKey] === b[idKey]; 
    } 

    if (_.isArray(srcValue)) { 
    value = _.unionWith(value, srcValue, comparator); 
    } 

    return _.set(src, key, value); 
} 

function magicUpdate(obj, src) { 
    _.forEach(src, function(value, key) { 
     updateProperty(obj, _.pick(src, key)); 
    }); 
    return obj; 
} 

Fiddle

+0

ご協力いただきありがとうございます。残念ながら、一度に複数の値を設定しようとしているときは機能しません(これは私が上記の例ではカバーしていなかったことを認めなければなりませんが、今のところです)。こちらをご覧ください:[https://jsfiddle.net/qyf3hbe1/](https://jsfiddle.net/qyf3hbe1/)。私は次の解決策を考え出した:[https://jsfiddle.net/r0507fyh/](https://jsfiddle.net/r0507fyh/) – niklas

+0

@niklasありがとう!私は、問題に対する可能な解決策で答えを更新しました。現在のメソッドを使用して、一度に1つのプロパティを更新します。それは動作するかもしれません!それが役に立てば幸い。 – acontell

+0

ありがとうございました。私はパフォーマンスが(あなたのソリューションの多くのループのために、私がプログラムしたカスタム関数より10倍遅いことがわかりました。ここを参照してくださいhttps://jsfiddle.net/r0507fyh/3/ – niklas

関連する問題