Javascript: сводка транзакций по нескольким свойствам объекта (groupby, sumby)
У меня есть массив валютных операций в разных валютах и биржах:
[
{buyCurr: "USD", sellCurr: "CAD", exchange: "Canada", buyunits: 1000, sellunits: 1200},
{buyCurr: "EUR", sellCurr: "CAD", exchange: "Europe", buyunits: 10000, sellunits: 15000},
{buyCurr: "GBP", sellCurr: "USD", exchange: "NYSE", buyunits: 5000, sellunits: 8000},
{buyCurr: "USD", sellCurr: "INR", exchange: "BSE", buyunits: 3000, sellunits: 180000},
{buyCurr: "USD", sellCurr: "JPY", exchange: "Japan", buyunits: 32000, sellunits: 1000000},
{buyCurr: "RUB", sellCurr: "INR", exchange: "Russia", buyunits: 1000, sellunits: 1100},
{buyCurr: "USD", sellCurr: "CNY", exchange: "China", buyunits: 3000, sellunits: 18000},
{buyCurr: "CNY", sellCurr: "INR", exchange: "China", buyunits: 10000, sellunits: 100000},
{buyCurr: "USD", sellCurr: "CAD", exchange: "Canada", buyunits: 1000, sellunits: 1200},
{buyCurr: "EUR", sellCurr: "CAD", exchange: "Europe", buyunits: 10000, sellunits: 15000},
{buyCurr: "GBP", sellCurr: "USD", exchange: "NYSE", buyunits: 5000, sellunits: 8000},
{buyCurr: "USD", sellCurr: "INR", exchange: "BSE", buyunits: 3000, sellunits: 180000},
{buyCurr: "USD", sellCurr: "JPY", exchange: "Japan", buyunits: 32000, sellunits: 1000000},
{buyCurr: "RUB", sellCurr: "INR", exchange: "Russia", buyunits: 1000, sellunits: 1100},
{buyCurr: "USD", sellCurr: "CNY", exchange: "China", buyunits: 3000, sellunits: 18000},
{buyCurr: "CNY", sellCurr: "INR", exchange: "China", buyunits: 10000, sellunits: 100000},
]
Я хочу обобщить свои позиции по биржам и валютам. Таким образом, результат будет выглядеть так:
[{
curency : USD,
exchange: Canada,
units: 2000
},
{
curency : CAD,
exchange: Canada,
units: -2400
},
{
curency : USD,
exchange: NYSE,
units: -12000
},
......and so on
]
Количество единиц на бирже: (сумма единиц покупки для валюты (i)) - (сумма единиц продажи (i)). Таким образом, доллар США будет зачтен только против доллара США.
Один из способов сделать это - использовать forEach для прохождения каждого экземпляра, фильтрации свойств, добавления / уменьшения buyCurr и sellCurr.
Я также попробовал lodash, но он позволяет мне использовать только одно свойство groupBy.
Есть ли лучший / более эффективный способ сделать это в JavaScript?
2 ответа
Вы можете сделать это с одним .reduce
также. Так как вы просто складываете и вычитаете значения, вы можете суммировать по ходу дела:
const input=[{buyCurr:"USD",sellCurr:"CAD",exchange:"Canada",buyunits:1e3,sellunits:1200},{buyCurr:"EUR",sellCurr:"CAD",exchange:"Europe",buyunits:1e4,sellunits:15e3},{buyCurr:"GBP",sellCurr:"USD",exchange:"NYSE",buyunits:5e3,sellunits:8e3},{buyCurr:"USD",sellCurr:"INR",exchange:"BSE",buyunits:3e3,sellunits:18e4},{buyCurr:"USD",sellCurr:"JPY",exchange:"Japan",buyunits:32e3,sellunits:1e6},{buyCurr:"RUB",sellCurr:"INR",exchange:"Russia",buyunits:1e3,sellunits:1100},{buyCurr:"USD",sellCurr:"CNY",exchange:"China",buyunits:3e3,sellunits:18e3},{buyCurr:"CNY",sellCurr:"INR",exchange:"China",buyunits:1e4,sellunits:1e5},{buyCurr:"USD",sellCurr:"CAD",exchange:"Canada",buyunits:1e3,sellunits:1200},{buyCurr:"EUR",sellCurr:"CAD",exchange:"Europe",buyunits:1e4,sellunits:15e3},{buyCurr:"GBP",sellCurr:"USD",exchange:"NYSE",buyunits:5e3,sellunits:8e3},{buyCurr:"USD",sellCurr:"INR",exchange:"BSE",buyunits:3e3,sellunits:18e4},{buyCurr:"USD",sellCurr:"JPY",exchange:"Japan",buyunits:32e3,sellunits:1e6},{buyCurr:"RUB",sellCurr:"INR",exchange:"Russia",buyunits:1e3,sellunits:1100},{buyCurr:"USD",sellCurr:"CNY",exchange:"China",buyunits:3e3,sellunits:18e3},{buyCurr:"CNY",sellCurr:"INR",exchange:"China",buyunits:1e4,sellunits:1e5}];
const output = input.reduce( ( sumObj, item ) => {
const key = `${ item.buyCurr } ${ item.exchange }`;
sumObj[ key ] = ( sumObj[ key ] || 0 ) + item.buyunits - item.sellunits;
return sumObj;
}, { /* initial sumObj is going to be an empty object */ } );
console.log( output );
Я не мог найти способ сделать все это за один проход, но с некоторой логикой в функции, вы можете запустить его три раза для:
- Купить транзакции
- Продать сделки
- Комбинированные транзакции
После чего вы получите свои краткие ответы. Не так хорошо, как за один проход, но предоставленная функция достаточно гибкая, чтобы обрабатывать все 3 случая: (Вам нужно прокрутить вниз, чтобы увидеть полный код)
const transactions = [
{buyCurr: "USD", sellCurr: "CAD", exchange: "Canada", buyunits: 1000, sellunits: 1200},
{buyCurr: "EUR", sellCurr: "CAD", exchange: "Europe", buyunits: 10000, sellunits: 15000},
{buyCurr: "GBP", sellCurr: "USD", exchange: "NYSE", buyunits: 5000, sellunits: 8000},
{buyCurr: "USD", sellCurr: "INR", exchange: "BSE", buyunits: 3000, sellunits: 180000},
{buyCurr: "USD", sellCurr: "JPY", exchange: "Japan", buyunits: 32000, sellunits: 1000000},
{buyCurr: "RUB", sellCurr: "INR", exchange: "Russia", buyunits: 1000, sellunits: 1100},
{buyCurr: "USD", sellCurr: "CNY", exchange: "China", buyunits: 3000, sellunits: 18000},
{buyCurr: "CNY", sellCurr: "INR", exchange: "China", buyunits: 10000, sellunits: 100000},
{buyCurr: "USD", sellCurr: "CAD", exchange: "Canada", buyunits: 1000, sellunits: 1200},
{buyCurr: "EUR", sellCurr: "CAD", exchange: "Europe", buyunits: 10000, sellunits: 15000},
{buyCurr: "GBP", sellCurr: "USD", exchange: "NYSE", buyunits: 5000, sellunits: 8000},
{buyCurr: "USD", sellCurr: "INR", exchange: "BSE", buyunits: 3000, sellunits: 180000},
{buyCurr: "USD", sellCurr: "JPY", exchange: "Japan", buyunits: 32000, sellunits: 1000000},
{buyCurr: "RUB", sellCurr: "INR", exchange: "Russia", buyunits: 1000, sellunits: 1100},
{buyCurr: "USD", sellCurr: "CNY", exchange: "China", buyunits: 3000, sellunits: 18000},
{buyCurr: "CNY", sellCurr: "INR", exchange: "China", buyunits: 10000, sellunits: 100000},
];
function positionSummary(transactions, summaryType) {
const newList = _(transactions)
.groupBy(summaryType === 'currency' ? summaryType : `${summaryType}Curr`)
.map(action => {
return _.groupBy(action, 'exchange');
})
.map(currency => {
return _.map(currency, exchangeArray => {
const currency = _.first(exchangeArray)[summaryType === 'currency' ? summaryType : `${summaryType}Curr`];
const exchange = _.first(exchangeArray)['exchange'];
let units = _(exchangeArray)
.sumBy(summaryType === 'currency' ? 'units' : `${summaryType}units`)
.valueOf();
units = _.multiply(units, summaryType === 'sell' ? -1 : 1);
return {
currency,
exchange,
units
};
});
})
.flatten()
.valueOf();
console.log(summaryType, newList);
return newList;
}
const buyUnitSummary = positionSummary(transactions, 'buy');
const sellUnitSummary = positionSummary(transactions, 'sell');
const allUnitSummary = _.concat(buyUnitSummary, sellUnitSummary);
const finalPositionSummary = positionSummary(allUnitSummary, 'currency');
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>