根据ID分配另一个数组中的某些字段,并根据值类型获得最终输出?

时间:2018-07-15 14:11:39

标签: javascript arrays node.js performance functional-programming

arr1 = [ 
         { id : 1,
           name : 'book1',
           description: 'some text'
         },
         { id : 2,
           name : 'book2',
           description: 'some text'
         },
         { id : 3,
           name : 'book3',
           description: 'some text'
         }
       ]

arr2 = [ 
         { id : 1,
           name : 'book1',
           type : 'thriller',
           publisher : 'some x',
           isbn : '2983457928435',
           date : '20-1-2014'
         },
         { id : 2,
           name : 'book2',
           type : 'action',
           publisher : 'some x',
           isbn : '2983457928435',
           date : '20-1-2014'
         },
         { id : 3,
           name : 'book3',
           type : 'thriller',
           publisher : 'some y',
           isbn : '2983457928435',
           date : '20-1-2014'
         }
       ]

我想基于id值将字段'type'和'publisher'分配给arr1,并并行地希望基于type的数组。

所需的输出是

arr3 = { 
   'thriller': [
     { id: 1, name: 'book1', type: 'thriller', publisher: 'some x', description: 'some text' }, 
     { id: 3, name: 'book3', type: 'thriller', publisher: 'some y', description: 'some text' }
   ], 
   'action': [
     { id: 2, name: 'book2', type: 'action', publisher: 'some x', description: 'some text' }] 
 }

性能是关键问题。我可以使用2个功能来实现上述目的,但我想用单个功能来实现。

Edit1:-在arr1和上面的字段中,我从database(mongodb)获取了其他字段。为简单起见,我包括了必需品。

目前,我通过以下2种功能实现此目标

功能1:-

let result = arr1.map((e) => {
        for(let element of arr2){
            if(e.id === element.id)
              { 
                e['type'] = element.type
                e['publisher'] = element.publisher
              }
        }
        return e
      })

功能2:-

function groupBy(objectArray, property) {
  return objectArray.reduce(function (acc, obj) {
    var key = obj[property];
    if (!acc[key]) {
      acc[key] = [];
    }
    acc[key].push(obj);
    return acc;
  }, {});
}

let output = groupBy(result, 'type');

我想要的是将两种功能结合在一起,并在单个函数调用中执行。

2 个答案:

答案 0 :(得分:1)

执行此操作后,我可以想象的唯一明显的性能问题就会出现:

arr1.reduce(function (acc, e) {
  for (let element of arr2) {
    /* ... */
  }
})

这个嵌套循环大大提高了迭代次数。对于arr1的每个元素,我们(潜在地)循环arr2的每个元素,从而为我们提供了arr1.length(最佳情况)和arr1.length * arr2.length(最坏情况)之间的多次迭代。 )。如果您使用的是非常大的数据集,那么这种差异可能会很明显...

通常,解决方案是将数组索引到对象或映射中,从而可以更快地进行查找。转换数组需要一些时间,但是循环会运行得更快。

这是我在项目符号中的建议:

  1. arr1转换为以下形式的索引bookDescriptions
    { id: { name, id, description } }
  2. arr2转换为以下形式的索引bookMetadata
    {id: { name, id, type, publisher } }
  3. 列出books dat并按ID将其合并到:
    [ { name, id, type, description, publisher } ]
  4. 使用您的groupBy进行最终分组

在代码中:

// Index an array of values by a unique property
const indexBy = (k, xs) => xs.reduce(
  (acc, x) => Object.assign(acc, { [x[k]]: x }),
  {}
);

// Group an array of objects by a certain property
const groupBy = (k, xs) => xs.reduce(
  (acc, x) => Object.assign(acc, { 
    [x[k]]: acc[x[k]] 
      ? acc[x[k]].concat(x)
      : [x]
  }), 
  {}
);

// Return array without duplicates
const uniques = xs => Array.from(new Set(xs));

// Merge two indexes by their keys using Object.assign
const mergeIndexes = (i1, i2) => 
  uniques([
    ...Object.keys(i1),
    ...Object.keys(i2)
  ]).map(k => Object.assign({}, i1[k], i2[k]));


const bookDescriptions = indexBy("id", getDescriptions());
const bookMetadata = indexBy("id", getMetadata());
const books = mergeIndexes(bookDescriptions, bookMetadata);

const booksByType = groupBy("type", books);

console.log(booksByType);

       
       
function getDescriptions() { return [{id:1,name:"book1",description:"some text"},{id:2,name:"book2",description:"some text"},{id:3,name:"book3",description:"some text"}]; };
function getMetadata() { return [{id:1,name:"book1",type:"thriller",publisher:"some x"},{id:2,name:"book2",type:"action",publisher:"some x"},{id:3,name:"book3",type:"thriller",publisher:"some y"}]; };

如果这仍然给您带来性能问题,您可以考虑微优化,例如合并功能,组合步骤等(但我不希望您需要这样做)

编辑:供参考,这是与1000本书的嵌套循环方法相比的索引方法的性能。我的机器的性能提高了大约10倍...

// Index an array of values by a unique property
const indexBy = (k, xs) => xs.reduce(
  (acc, x) => Object.assign(acc, { [x[k]]: x }),
  {}
);

// Group an array of objects by a certain property
const groupBy = (k, xs) => xs.reduce(
  (acc, x) => Object.assign(acc, { 
    [x[k]]: acc[x[k]] 
      ? acc[x[k]].concat(x)
      : [x]
  }), 
  {}
);

// Return array without duplicates
const uniques = xs => Array.from(new Set(xs));

// Merge two indexes by their keys using Object.assign
const bookMerger = (
  { name, id, description},
  { type, publisher }) => (
    { name, id, description, type, publisher }
  );
  
const mergeIndexes = (i1, i2) => 
  uniques([
    ...Object.keys(i1),
    ...Object.keys(i2)
  ]).map(k => bookMerger(i1[k], i2[k]));


console.time("indexed approach");
const bookDescriptions = indexBy("id", getDescriptions());
const bookMetadata = indexBy("id", getMetadata());
const books = mergeIndexes(bookDescriptions, bookMetadata);

const booksByType = groupBy("type", books);
console.timeEnd("indexed approach");

console.time("nested loop approach");
let result = getDescriptions().reduce(function (acc, e) {
  let arr2 = getMetadata();
	for(let element of arr2){
            if(e.id === element.id)
              { 
                e['type'] = element.type
                e['publisher'] = element.publisher
                break
              }
        }
           var key = e['type'];
	   if (!acc[key]) {
           acc[key] = [];
            }
          acc[key].push(e);
          return acc;
    
        }, {});
console.timeEnd("nested loop approach");
       
function getDescriptions() { return Array.from(
  Array(1000),
  (_, i) => (
    {id:i, name:"book1", description:"some text"})
  );
}

function getMetadata() { return Array.from(
  Array(1000),
  (_, i) => (
    {id:i,name:"book1",type:"thriller",publisher:"some x"}
  ));
};

答案 1 :(得分:0)

arr1 = [ 
         { id : 1,
           name : 'book1',
           description : 'Some text here'
         },
         { id : 2,
           name : 'book2',
           description : 'Some text here'
         },
         { id : 3,
           name : 'book3',
           description : 'Some text here'
         }
       ]

arr2 = [ 
         { id : 1,
           name : 'book1',
           type : 'thriller',
           publisher : 'some x'
         },
         { id : 2,
           name : 'book2',
           type : 'action',
           publisher : 'some x'
         },
         { id : 3,
           name : 'book3',
           type : 'thriller',
           publisher : 'some y'
         }
       ]

let result = arr1.reduce(function (acc, e) {
	for(let element of arr2){
            if(e.id === element.id)
              { 
                e['type'] = element.type
                e['publisher'] = element.publisher
                break
              }
        }
           var key = e['type'];
	   if (!acc[key]) {
           acc[key] = [];
            }
          acc[key].push(e);
          return acc;
    
        }, {});
console.log(result)

我最终得到了上述解决方案。