2016-07-20 107 views
2

我有以下2個JavaScript對象:合併嵌套的對象,而變異

let o1 = { 
    entities: { 
    1: { 
     text: "fooo", 
     nested: { 
     ids: [1, 2, 3], 
     flag: true 
     } 
    } 
    } 
}; 

let o2 = { 
    ids: [4, 5, 6] 
} 

我想將它們合併沒有變異他們,得到它看起來像這樣的對象:

let o3 = { 
    entities: { 
    1: { 
     text: "fooo", 
     nested: { 
     ids: [1, 2, 3, 4, 5, 6], 
     flag: true 
     } 
    } 
    } 
}; 

有可能是entities,但只有entityId定義的那個應該受到影響。 我試了一下:

let entityId = 1; 

let o3 = Object.assign({}, o1, { 
     entities: Object.assign({}, o1.entities, { 
      [entityId]: Object.assign({}, o1.entities[entityId].nested, 
      { ids: [...o1.entities[entityId].nested.ids, ...o2.ids] } 
     ) 
     }) 
     }); 

的問題是,text: "fooo",nested:完全消失。

我的意思是對的嗎?這個代碼是否可以優化?

+1

*「得到一個看起來像這樣的對象:」* - 您忘記包含示例輸出。你是否在拍攝與「o1」相同的結構,但添加了額外的ID到陣列中? – nnnnnn

+0

對不起,我改變了。是extactly – Daskus

+0

您的示例輸入具有'entities'(複數)屬性,但只有單個'1'屬性。真正的數據是否有「2」,「3」等,如果是的話,那麼應該把「o2」中的id與哪一個合併? – nnnnnn

回答

0

如果你知道你的數據不會包括任何日期的方法,你可以使用JSON深克隆招:

let o3 = JSON.parse(JSON.stringify(o1)); 
Array.prototype.push.apply(o3.entities[1].nested.ids, o2.ids); 
+0

在我的真實代碼中,我有日期,所以你的aproach不會工作...我試圖簡化代碼。 – Daskus

+0

它實際上與日期一起工作,但日期對象(即不是時間串等)將被轉換爲ISO字符串。要將它們返回到日期對象,您可以簡單地遍歷o3(IMO時間戳通常比日期對象更好,直到您無論如何需要它)。除非你正在處理大量的數據集,否則這個方法比試圖編寫你自己的解析器要容易得多。 – som

+0

或者看看lodash或jquery.clone – som

3

既然你標記的終極版,我會假設你使用的反應,並建議您使用的不變性幫手 - https://facebook.github.io/react/docs/update.html

您的代碼應該是這樣的:

let o3 = update(o1, { 
    entities: { 
     [entityId]: { 
      nested: { 
       ids: { 
        $push: [4, 5, 6] 
       } 
      } 
     } 
    } 
}) 

ES6

你的邏輯是正確的,但你缺少在代碼中nested對象:

let o3 = Object.assign({}, o1, { 
    entities: Object.assign({}, o1.entities, { 
     [entityId]: Object.assign({}, o1.entities[entityId], { 
      nested : Object.assign({}, o1.entities[entityId].nested ,{ 
      ids: [...o1.entities[entityId].nested.ids, ...o2.ids] }) 
     } 
    ) 
    }) 
    }); 
+0

我正在使用Angular2,我會檢查是否有這樣的幫手。在此先感謝 – Daskus

+0

@Daskus無論如何,使用Facebook的幫手可能沒有錯,因爲它是一個獨立的模塊 - https://facebook.github.io/immutable-js/ –

+0

哦,你是對的!但是在將狀態保存到localStorage時不使用Immutable.js是一個問題? – Daskus

0

那麼,你的第一個問題是該位:

{ 
     [entityId]: Object.assign({}, o1.entities[entityId].nested, 

,你將o1.entities[1].nested的值複製到密鑰o3.entities[1]中,從而丟失o1.entities[1]中保存的對象中的其他密鑰。

這很難看,因爲正如其他評論所指出的,這不是很漂亮的代碼。

當然,您想(並且打算)使用o1.entities[entityId]

你有另外一個問題,雖然,因爲如果你只是在做對的o1的一部分,你是目前在一個感興趣深副本Object.assign()將複製而不是創建完全新鮮的對象的對象引用, 。因此,如果您稍後修改o3.entities[1].text的值,那麼您也將變異爲o1.entities[1]

+0

我不這麼認爲,因爲我傳遞一個新的空對象作爲第一個參數,對象將被複制並且不會變異:Object.assign({},...) – Daskus

+0

當一個對象是通過'Object.assign()'拷貝的,它包含的任何值本身就是對象僅僅被* reference *拷貝。所以: –

+0

'let a = {foo:{}};' 'let b = Object.assign({},a}; //將a的內容複製到b'中 'b.bar = {} ; // a不受影響,因爲b是一個獨立副本'' 'b.foo.narf = 1; // a.foo受到影響,因爲它引用與b.foo相同的對象,而控制檯爲 。日誌(a.foo); //打印「{narf:1}」 –

0

看起來像你錯過了一層合併 - 你的示例代碼有entities.1.ids而不是entities.1.nested.ids。嘗試

let entityId = 1; 

let o3 = Object.assign(
    {}, 
    o1, 
    { 
    entities: Object.assign(
     {}, 
     o1.entities, 
     { 
     [entityId]: Object.assign(
      {}, 
      o1.entities[entityId], 
      Object.assign(
      {}, 
      o1.entities[entityId].nested, 
      { ids: [...o1.entities[entityId].nested.ids, ...o2.ids] } 
     ) 
     ) 
     } 
    ) 
    } 
); 

在這一點上,你可能想建立從每個Object.assign調用一些中間變量,所以您可以跟蹤所有好一點的。