簡體   English   中英

map使用相同的索引添加/減少兩個數組對象

[英]map add/reduce two array object with same index

我有兩個數組對象如下:

var arr1 = [
    {
        name: 1,
        value: 10
    },
    {
        name: 2,
        value: 15
    }
]

var arr2 = [
    {
        name: 3,
        value: 5
    },
    {
        name: 4,
        value: 3
    }
]

我想重新定義密鑰並使用相同的索引減少每個數據。

輸出:

var arr1 = [
    {
        itemLabel: 1,
        itemValue: 5
    }, 
    {
        itemLabel: 2,
        itemValue: 12
    }
]

我現在正在做如下:

formatData = arr1.map((row, index) => ({
    itemLabel: arr1.name,
    itemValue: arr1.value - arr2[index].value
}))

這樣做有什么更好的解決方案嗎?

你的代碼很好,你也可以使用遞歸:

 var arr1 =[{ name: 1, value: 10 }, { name: 2, value: 15 }]; var arr2= [{ name: 3, value: 5 }, { name: 4, value: 3 }] const createObject=(arr1,arr2,ret=[])=>{ if(arr1.length!==arr2.length){ throw("Arrays should be the same length.") } const item = { itemLabel: arr1[0].name, itemValue: arr1[0].value - arr2[0].value }; if(arr1.length===0){ return ret; }; return createObject(arr1.slice(1),arr2.slice(1),ret.concat(item)); } console.log(createObject(arr1,arr2)); 

實現mapreduce兩個函數都必須在其作用域之外使用arr1或arr2(不作為參數傳遞給它),因此嚴格來說不是純粹的。 但你可以通過部分應用輕松解決它:

 var arr1 =[{ name: 1, value: 10 }, { name: 2, value: 15 }]; var arr2= [{ name: 3, value: 5 }, { name: 4, value: 3 }]; const mapFunction = arr2 => (item,index) => { return { itemLabel: item.name, itemValue: item.value - arr2[index].value } } var createObject=(arr1,arr2,ret=[])=>{ if(arr1.length!==arr2.length){ throw("Arrays should be the same length.") } const mf = mapFunction(arr2); return arr1.map(mf); } console.log(createObject(arr1,arr2)); 

但正如CodingIntrigue在評論中提到的那樣:這些都不比你已經做過的“更好”。

孤膽英雄

一個簡單的遞歸程序,可以處理單個函數中的所有內容。 這里有一個明顯的混合問題,這會損害功能的整體可讀性。 我們將在下面看到針對此問題的一種此類補救措施

 const main = ([x, ...xs], [y, ...ys]) => x === undefined || y === undefined ? [] : [ { itemLabel: x.name, itemValue: x.value - y.value } ] .concat (main (xs, ys)) const arr1 = [ { name: 1, value: 10 }, { name: 2, value: 15 } ] const arr2 = [ { name: 3, value: 5 }, { name: 4, value: 3 } ] console.log (main (arr1, arr2)) // [ { itemLabel: 1, itemValue: 5 }, // { itemLabel: 2, itemValue: 12 } ] 


用類型思考

答案的這一部分受到Monoid類別的類型理論的影響 - 我不會對它進行太深入的研究,因為我認為代碼應該能夠證明自己。

所以我們在問題中有兩種類型:我們稱它們為Foo和Bar

  • Foo - 有namevalue字段
  • Bar - 有itemLabelitemValue字段

我們可以代表我們想要的“類型”,但我選擇了一個構造對象的簡單函數

const Foo = (name, value) =>
  ({ name
   , value
   })

const Bar = (itemLabel, itemValue) =>
  ({ itemLabel
   , itemValue
   })

制作一種類型的價值觀

要構造我們類型的新值,我們只需將函數應用於字段值

const arr1 =
  [ Foo (1, 10), Foo (2, 15) ]

const arr2 =
  [ Foo (3, 5), Foo (4, 3) ]

讓我們看看目前為止的數據

console.log (arr1)
// [ { name: 1, value: 10 },
//   { name: 2, value: 15 } ]

console.log (arr2)
// [ { name: 3, value: 5 },
//   { name: 4, value: 3 } ]

一些高層次的規划

我們有一個良好的開端。 我們有兩個Foo值數組。 我們的目標是通過從每個數組中取一個Foo值,將它們組合起來(后面會詳細介紹),然后轉到下一對數組來完成兩個數組的工作。

const zip = ([ x, ...xs ], [ y, ...ys ]) =>
  x === undefined || y === undefined
    ? []
    : [ [ x, y ] ] .concat (zip (xs, ys))

console.log (zip (arr1, arr2))
// [ [ { name: 1, value: 10 },
//     { name: 3, value: 5 } ],
//   [ { name: 2, value: 15 },
//     { name: 4, value: 3 } ] ]

結合價值: concat

通過將Foo值正確地組合在一起,我們現在可以更多地關注組合過程。 在這里,我將定義一個通用的concat ,然后在我們的Foo類型上實現它

// generic concat
const concat = (m1, m2) =>
  m1.concat (m2)

const Foo = (name, value) =>
  ({ name
   , value
   , concat: ({name:_, value:value2}) =>
       // keep the name from the first, subtract value2 from value
       Foo (name, value - value2)
   })

console.log (concat (Foo (1, 10), Foo (3, 5)))
// { name: 1, value: 5, concat: [Function] }

concat聽起來很熟悉嗎? 數組和字符串也是Monoid類型!

concat ([ 1, 2 ], [ 3, 4 ])
// [ 1, 2, 3, 4 ]

concat ('foo', 'bar')
// 'foobar'

高階函數

所以現在我們有辦法將兩個Foo值組合在一起。 保留第一個Foo的name ,並減去value屬性。 現在我們在“壓縮”結果中將它應用於每一對。 功能程序員喜歡高階函數,所以你會欣賞這種高階和諧

const apply = f => xs =>
  f (...xs)

zip (arr1, arr2) .map (apply (concat))
// [ { name: 1, value: 5, concat: [Function] },
//   { name: 2, value: 12, concat: [Function] } ]

轉換類型

所以現在我們有了具有正確namevalue的Foo值,但我們希望我們的最終答案是Bar值。 我們需要一個專門的構造函數

Bar.fromFoo = ({ name, value }) =>
  Bar (name, value)

Bar.fromFoo (Foo (1,2))
// { itemLabel: 1, itemValue: 2 }

zip (arr1, arr2)
  .map (apply (concat))
  .map (Bar.fromFoo)
// [ { itemLabel: 1, itemValue: 5 },
//   { itemLabel: 2, itemValue: 12 } ]

努力工作會有成果

一個美麗,純粹的功能表達。 我們的課程閱讀得非常好; 由於聲明式風格,數據的流動和轉換很容易遵循。

// main :: ([Foo], [Foo]) -> [Bar]
const main = (xs, ys) =>
  zip (xs, ys)
    .map (apply (concat))
    .map (Bar.fromFoo)

當然還有完整的代碼演示

 const Foo = (name, value) => ({ name , value , concat: ({name:_, value:value2}) => Foo (name, value - value2) }) const Bar = (itemLabel, itemValue) => ({ itemLabel , itemValue }) Bar.fromFoo = ({ name, value }) => Bar (name, value) const concat = (m1, m2) => m1.concat (m2) const apply = f => xs => f (...xs) const zip = ([ x, ...xs ], [ y, ...ys ]) => x === undefined || y === undefined ? [] : [ [ x, y ] ] .concat (zip (xs, ys)) const main = (xs, ys) => zip (xs, ys) .map (apply (concat)) .map (Bar.fromFoo) const arr1 = [ Foo (1, 10), Foo (2, 15) ] const arr2 = [ Foo (3, 5), Foo (4, 3) ] console.log (main (arr1, arr2)) // [ { itemLabel: 1, itemValue: 5 }, // { itemLabel: 2, itemValue: 12 } ] 


備注

我們上面的程序是用.map - .map鏈實現的,這意味着多次處理和創建中間值。 我們還在zip調用中創建了[[x1,y1], [x2,y2], ...]的中間數組。 類別理論為我們提供了諸如等式推理之類的東西,因此我們可以用m.map(compose(f,g))替換m.map(f).map(g) m.map(compose(f,g))並獲得相同的結果。 所以還有改進空間的空間,但我認為這足以削減你的牙齒並開始以不同的方式思考問題。

為了使您的解決方案更具功能性,您需要將匿名函數更改為純(匿名)函數。

純函數是一個函數,在給定相同輸入的情況下,它將始終返回相同的輸出

匿名函數取決於可變變量arr1arr2 這意味着它取決於系統狀態。 所以它不適合純函數規則。

以下可能不是最好的實現,但我希望它能給你一個想法..

讓它變得純凈

為了使它純粹,我們可以將變量作為參數傳遞給函數

const mapWithObject = (obj2, obj1, index) => ({
    itemLabel: obj1.name,
    itemValue: obj1.value - obj2[index].value
})

// example call
const result = mapWithObject(arr2, arr1[0], 0)

好的,但現在該功能不再適合map了,因為它需要3個參數而不是2個...

我們來吧

const mapWithObject = obj2 => (obj1, index) => ({
  itemLabel: obj1.name,
  itemValue: obj1.value - obj2[index].value
})

const mapObject_with_arr2 = mapWithObject(arr2)

// example call
const result = mapObject_with_arr2(arr1[0], 0)

完整代碼

 const arr1 = [{ name: 1, value: 10 }, { name: 2, value: 15 } ] const arr2 = [{ name: 3, value: 5 }, { name: 4, value: 3 } ] const mapWithObject = obj2 => (obj1, index) => ({ itemLabel: obj1.name, itemValue: obj1.value - obj2[index].value }) const mapObject_with_arr2 = mapWithObject(arr2) const mappedObject = arr1.map(mapObject_with_arr2) console.log(mappedObject) 

如果您不太關心性能,但想要進一步區分您的關注點,您可以使用此方法:

  • 定義一個在arr1arr2之間進行“配對”的函數

     [a, b, c] + [1, 2, 3] -> [ [ a, 1 ], [ b, 2 ], [ c, 3 ] ] 
  • 定義一個清楚地顯示兩個對象的合並策略的函數

     { a: 1, b: 10 } + { a: 2, b: 20 } -> { a: 1, b: -10 } 
  • 定義組成兩者的簡單助手,這樣您就可以傳遞原始數組,並在一次函數調用中返回所需的輸出。

這是一個例子:

 var arr1=[{name:1,value:10},{name:2,value:15}],arr2=[{name:3,value:5},{name:4,value:3}]; // This is a very general method that bundles two // arrays in an array of pairs. Put it in your utils // and you can use it everywhere const pairs = (arr1, arr2) => Array.from( { length: Math.max(arr1.length, arr2.length) }, (_, i) => [ arr1[i], arr2[i] ] ); // This defines our merge strategy for two objects. // Ideally, you should give it a better name, based // on what the objects represent const merge = (base, ext) => ({ itemLabel: base.name, itemValue: base.value - ext.value }); // This is a helper that "applies" our merge method // to an array of two items. const mergePair = ([ base, ext ]) => merge(base, ext); // Another helper that composes `pairs` and `mergePair` // to allow you to pass your original data. const mergeArrays = (arr1, arr2) => pairs(arr1, arr2).map(mergePair); console.log(mergeArrays(arr1, arr2)); 

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM