Home > Net >  How does one, from an array of objects, collect and aggregate the merged variant of all objects with
How does one, from an array of objects, collect and aggregate the merged variant of all objects with

Time:04-30

const portfolio = [
  { name: 'Mark', stock: 'FB' },
  { name: 'Steve', stock: 'AAPL' },
  { name: 'Tim', stock: 'AAPL' },
  { name: 'Steve', stock: 'MSFT' },
  { name: 'Bill', stock: 'MSFT' },
  { name: 'Bill', stock: 'AAPL' },
];

// Output
const shareholder = [
  { stock: 'AAPL', name: ['Steve', 'Bill', 'Tim'], count: 3 },
  { stock: 'MSFT', name: ['Steve', 'Bill'], count: 2 },
  { stock: 'FB', name: ['Mark'], count: 1 },
];

if I create one function which take input array as param and this will return output array in jS

CodePudding user response:

One way using reduce and Object.values

const portfolio = [{
    name: 'Mark',
    stock: 'FB'
  },
  {
    name: 'Steve',
    stock: 'AAPL'
  },
  {
    name: 'Tim',
    stock: 'AAPL'
  },
  {
    name: 'Steve',
    stock: 'MSFT'
  },
  {
    name: 'Bill',
    stock: 'MSFT'
  },
  {
    name: 'Bill',
    stock: 'AAPL'
  },
];

const result = Object.values(portfolio.reduce((res, {
  stock,
  name
}) => {
  const existing = res[stock] || {
    stock,
    names: [],
    count: 0
  }
  res[stock] = {
    stock,
    names: [...existing.names, name],
    count: existing.count   1
  }
  return res
}, {}))

console.log(result)

CodePudding user response:

From the above comment ...

"What the OP wants is filtering and grouping array items (by a specific key) together with value aggregation of some/one other key/s. One usually would use a reduce based approach. One question though ... what is the additional count value good for when one has this information already in any item's item.name.length (or at least rename count to nameCount)."

... used techniques/methods ...

const portfolio = [
  { name: 'Mark', stock: 'FB' },
  { name: 'Steve', stock: 'AAPL' },
  { name: 'Tim', stock: 'AAPL' },
  { name: 'Steve', stock: 'MSFT' },
  { name: 'Bill', stock: 'MSFT' },
  { name: 'Bill', stock: 'AAPL' },
];
const shareholderList = Object.values( // get only the values from ...

  // ... create an index/map of stock specific shareholder items/objects
  portfolio.reduce((stockIndex, { name, stock }) => {

    // access an already existing object or
    // create a new grouped (by `stock` value) to be merged and aggregated object.
    const groupedMerger = (stockIndex[stock] ??= { stock, names: [], nameCount: 0 });

    // aggregate list of `stock` specific shareholder names.
    groupedMerger.names.push(name);
    // increment count of `stock` specific shareholder names.
      groupedMerger.nameCount;

    // the programmatically built index/map of stock specific shareholder items/objects.
    return stockIndex;
  }, {})

).sort((a, b) => b.nameCount - a.nameCount); // sort shareholder items by theirs `nameCount`s.

console.log({ shareholderList });
.as-console-wrapper { min-height: 100%!important; top: 0; }

And in order to demonstrate how each part works and how everything works together, the above main approach will be compacted into a (re-usable) function statement.

function aggregateStockIndex(index, { name, stock }) {
  const groupedMerger = (index[stock] ??= { stock, names: [], nameCount: 0 });

  groupedMerger.names.push(name);
    groupedMerger.nameCount;

  return index;
}

const portfolio = [
  { name: 'Mark', stock: 'FB' },
  { name: 'Steve', stock: 'AAPL' },
  { name: 'Tim', stock: 'AAPL' },
  { name: 'Steve', stock: 'MSFT' },
  { name: 'Bill', stock: 'MSFT' },
  { name: 'Bill', stock: 'AAPL' },
];
const stockIndex = portfolio
  .reduce(aggregateStockIndex, {});

const shareholderList = Object.values(

  // stockIndex
  portfolio.reduce(aggregateStockIndex, {})

).sort((a, b) => b.nameCount - a.nameCount);


console.log({
  stockIndex,
  shareholderList,
});
.as-console-wrapper { min-height: 100%!important; top: 0; }

CodePudding user response:

I shared a function named groupPortfolio that takes a portfolio as an argument expecting to be a list of people holding a share of a stock.

The function first groups those people as a map binding each stock to which holders it belong to and eventually uses that map to create the final array as a list of stocks where each one has a list of people holding its share and the corresponding amount of people in that list.

function groupPortfolio(portfolio){

  //groups the portfolio item in [stock] => shareholders[]
  let grouped = {};
  for(let o of portfolio){
    if( !Object.keys(grouped).includes(o.stock) )
      grouped[o.stock] = [];
    grouped[o.stock].push( o.name );
  }
  
  //creates the shareholders array starting from the grouped stocks
  let shareholders = [];
  for( let stockGroup of Object.keys(grouped) ){
    shareholders.push(
      { stock: stockGroup, name: grouped[stockGroup], count: grouped[stockGroup].length }
    );
  }

  return shareholders;  
}

const portfolio1 = [
  {name: 'Mark', stock: 'FB'},
  {name: 'Steve', stock: 'AAPL'},
  {name: 'Tim', stock: 'AAPL'},
  {name: 'Steve', stock: 'MSFT'},
  {name: 'Bill', stock: 'MSFT'},
  {name: 'Bill', stock: 'AAPL'},
];

let shareholder1 = groupPortfolio(portfolio1);
console.log( shareholder1 );
/*
0:
  stock: "FB"
  name: ['Mark']
  count: 1
1:
  stock: "AAPL"
  name: (3) ['Steve', 'Tim', 'Bill']
  count: 3
2:
  stock: "MSFT"
  name: (2) ['Steve', 'Bill']
  count: 2
*/

CodePudding user response:

You can use two simple for loops to convert the first array into the second array.


Working Example:

const portfolio = [
  {name: 'Mark', stock: 'FB'},
  {name: 'Steve', stock: 'AAPL'},
  {name: 'Tim', stock: 'AAPL'},
  {name: 'Steve', stock: 'MSFT'},
  {name: 'Bill', stock: 'MSFT'},
  {name: 'Bill', stock: 'AAPL'},
];

let shareholder = [];

// CYCLE THROUGH PORTFOLIO
for (let i = 0; i < portfolio.length; i  ) {
  
  // DETERMINE IF STOCK ENTRY ALREADY EXISTS
  let stockIndex = shareholder.length;

  for (let j = 0; j < shareholder.length; j  ) {
  
    if (portfolio[i].stock === shareholder[j].stock) {
    
      stockIndex = j;
    }
  }
  
  // ADD NEW ENTRY IF STOCK ENTRY DOES NOT EXIST
  if (stockIndex === shareholder.length) {
  
    shareholder[stockIndex] = {stock: portfolio[i].stock, name: [], count: 0};
  }
  
  // ADD DETAILS TO NEW OR EXISTING STOCK ENTRY
  shareholder[stockIndex].name.push(portfolio[i].name);
  shareholder[stockIndex].count  ;
}

console.log(shareholder);

CodePudding user response:

You could achieve this in two steps

  1. First create an object with stock as the key name to find out all the records with unique stock.
  2. Loop over the object created in step#1 to convert to an Array structure

I couldn't think of ay solution which could achieve it in single iteration.

Please see the code snippet below.

const portfolio = [
{name: 'Mark', stock: 'FB'},
{name: 'Steve', stock: 'AAPL'},
{name: 'Tim', stock: 'AAPL'},
{name: 'Steve', stock: 'MSFT'},
{name: 'Bill', stock: 'MSFT'},
{name: 'Bill', stock: 'AAPL'},
];

let shareholderObj = {};
let shareholderArr = [];

portfolio.forEach((el) => {
  const stock = el.stock
  if(shareholderObj[stock]){
    shareholderObj[stock].name.push(el.name)
  }
  else{
    shareholderObj[stock] = {
      name: [el.name]
    }
  }
})

for (let [key, value] of Object.entries(shareholderObj)) {
    shareholderArr.push({
      stock: key,
      name: value.name,
      count: value.name.length
    })
}

console.log(shareholderArr)

  • Related