我想转换此输入
[
{ country: 'France', value: 100 },
{ country: 'France', value: 100 },
{ country: 'Romania', value: 500 },
{ country: 'England', value: 400 },
{ country: 'England', value: 400 },
{ country: 'Spain', value: 130 },
{ country: 'Albania', value: 4 },
{ country: 'Hungary', value: 3 }
]
进入输出
[
{ country: 'England', value: 800 },
{ country: 'Romania', value: 500 },
{ country: 'France', value: 200 },
{ country: 'Spain', value: 130 },
{ country: 'Other', value: 8 }
]
基本上,这是前四个+其他国家/地区的 值之和 。
我正在将JavaScript与ramdajs一起使用,而我只能在 somehow cumbersome way so far中使用它。
我正在寻找一个优雅的解决方案:那里的功能程序员可以提供他们的解决方案吗?还是对ramda方法有帮助的想法?
答案 0 :(得分:4)
(每个步骤都获得上一步的输出。所有内容都将放在最后。)
步骤1:获取总和图
您可以对此进行转换:
[
{ country: 'France', value: 100 },
{ country: 'France', value: 100 },
{ country: 'Romania', value: 500 },
{ country: 'England', value: 400 },
{ country: 'England', value: 400 },
{ country: 'Spain', value: 130 },
{ country: 'Albania', value: 4 },
{ country: 'Hungary', value: 3 }
]
对此:
{
Albania: 4,
England: 800,
France: 200,
Hungary: 3,
Romania: 500,
Spain: 130
}
与此:
const reducer = reduceBy((sum, {value}) => sum + value, 0);
const reduceCountries = reducer(prop('country'));
步骤2:将其转换回已排序的数组
[
{ country: "Hungary", value: 3 },
{ country: "Albania", value: 4 },
{ country: "Spain", value: 130 },
{ country: "France", value: 200 },
{ country: "Romania", value: 500 },
{ country: "England", value: 800 }
]
您可以执行以下操作:
const countryFromPair = ([country, value]) => ({country, value});
pipe(toPairs, map(countryFromPair), sortBy(prop('value')));
第3步:创建两个子组,即非排名前4的国家和排名前4的国家
[
[
{ country: "Hungary", value: 3},
{ country: "Albania", value: 4}
],
[
{ country: "Spain", value: 130 },
{ country: "France", value: 200 },
{ country: "Romania", value: 500 },
{ country: "England", value: 800 }
]
]
您可以这样做:
splitAt(-4)
第4步:合并第一个子组
[
[
{ country: "Others", value: 7 }
],
[
{ country: "Spain", value: 130 },
{ country: "France", value: 200 },
{ country: "Romania", value: 500 },
{ country: "England", value: 800 }
]
]
与此:
over(lensIndex(0), compose(map(countryFromPair), toPairs, reduceOthers));
第5步:展平整个阵列
[
{ country: "Others", value: 7 },
{ country: "Spain", value: 130 },
{ country: "France", value: 200 },
{ country: "Romania", value: 500 },
{ country: "England", value: 800 }
]
使用
flatten
完整的工作示例
const data = [
{ country: 'France', value: 100 },
{ country: 'France', value: 100 },
{ country: 'Romania', value: 500 },
{ country: 'England', value: 400 },
{ country: 'England', value: 400 },
{ country: 'Spain', value: 130 },
{ country: 'Albania', value: 4 },
{ country: 'Hungary', value: 3 }
];
const reducer = reduceBy((sum, {value}) => sum + value, 0);
const reduceOthers = reducer(always('Others'));
const reduceCountries = reducer(prop('country'));
const countryFromPair = ([country, value]) => ({country, value});
const top5 = pipe(
reduceCountries,
toPairs,
map(countryFromPair),
sortBy(prop('value')),
splitAt(-4),
over(lensIndex(0), compose(map(countryFromPair), toPairs, reduceOthers)),
flatten
);
top5(data)
答案 1 :(得分:3)
这是一种方法:
const combineAllBut = (n) => pipe(drop(n), pluck(1), sum, of, prepend('Others'), of)
const transform = pipe(
groupBy(prop('country')),
map(pluck('value')),
map(sum),
toPairs,
sort(descend(nth(1))),
lift(concat)(take(4), combineAllBut(4)),
map(zipObj(['country', 'value']))
)
const countries = [{ country: 'France', value: 100 }, { country: 'France', value: 100 }, { country: 'Romania', value: 500 }, { country: 'England', value: 400 }, { country: 'England', value: 400 }, { country: 'Spain', value: 130 }, { country: 'Albania', value: 4 }, { country: 'Hungary', value: 3 }]
console.log(transform(countries))
<script src="https://bundle.run/ramda@0.26.1"></script>
<script>
const {pipe, groupBy, prop, map, pluck, sum, of, prepend, toPairs, sort, descend, nth, lift, concat, take, drop, zipObj} = ramda
</script>
除了一个复杂的行(lift(concat)(take(4), combineAllBut(4))
)和关联的辅助函数(combineAllBut
)以外,这是一组简单的转换。该辅助函数可能不在该函数之外有用,因此将其内联为lift(concat)(take(4), pipe(drop(4), pluck(1), sum, of, prepend('Others'), of))
是完全可以接受的,但是我发现结果函数有点难以阅读。
请注意,该函数将返回类似[['Other', 7]]
的格式,这是毫无意义的格式,因为事实是我们将concat
排在前四位。因此,至少有一些理由要删除最后的of
并将concat
替换为flip(append)
。我之所以没有这样做,是因为该辅助函数仅在该管道的上下文中没有任何意义。但是我会理解是否有人会选择其他方式。
我喜欢此功能的其余部分,它似乎很适合Ramda管道样式。但是该辅助功能在某种程度上破坏了它。我很想听听简化它的建议。
然后来自customcommander的答案证明了我可以采用的简化方法,即使用reduceBy
而不是上述方法中的groupBy -> map(pluck) -> map(sum)
舞蹈。这肯定可以改善。
const combineAllBut = (n) => pipe(drop(n), pluck(1), sum, of, prepend('Others'), of)
const transform = pipe(
reduceBy((a, {value}) => a + value, 0, prop('country')),
toPairs,
sort(descend(nth(1))),
lift(concat)(take(4), combineAllBut(4)),
map(zipObj(['country', 'value']))
)
const countries = [{ country: 'France', value: 100 }, { country: 'France', value: 100 }, { country: 'Romania', value: 500 }, { country: 'England', value: 400 }, { country: 'England', value: 400 }, { country: 'Spain', value: 130 }, { country: 'Albania', value: 4 }, { country: 'Hungary', value: 3 }]
console.log(transform(countries))
<script src="https://bundle.run/ramda@0.26.1"></script>
<script>
const {pipe, reduceBy, prop, map, pluck, sum, of, prepend, toPairs, sort, descend, nth, lift, concat, take, drop, zipObj} = ramda
</script>
答案 2 :(得分:2)
我尝试一下,尝试将其功能用于大多数事情。并保持单个pipe
const f = pipe(
groupBy(prop('country')),
map(map(prop('value'))),
map(sum),
toPairs(),
sortBy(prop(1)),
reverse(),
addIndex(map)((val, idx) => idx<4?val:['Others',val[1]]),
groupBy(prop(0)),
map(map(prop(1))),
map(sum),
toPairs(),
map(([a,b])=>({'country':a,'value':b}))
)
但是,我认为它不可读。
答案 3 :(得分:1)
我认为您可以通过在缩小数组之前对数组进行分割来稍微简化groupOthersKeeping
(以ramda表示),如下所示:
const groupOthersKeeping = contriesToKeep => arr => [
...slice(0, contriesToKeep, arr),
reduce(
(acc, i) => ({ ...acc, value: acc.value + i.value }),
{ country: 'Others', value: 0 },
slice(contriesToKeep, Infinity, arr)
)
]
答案 4 :(得分:1)
使用更多的ramda函数,但不确定是否更好:
let country = pipe(
groupBy(prop('country')),
map(pluck('value')),
map(sum)
)([
{ country: 'France', value: 100 },
{ country: 'France', value: 100 },
{ country: 'Romania', value: 500 },
{ country: 'England', value: 400 },
{ country: 'England', value: 400 },
{ country: 'Spain', value: 130 },
{ country: 'Albania', value: 4 },
{ country: 'Hungary', value: 3 }
]);
let splitCountry = pipe(
map((k) => ({country: k, value: country[k]})),
sortBy(prop('value')),
reverse,
splitAt(4)
)(keys(country));
splitCountry[0].push({country: 'Others', value: sum(map(prop('value'))(splitCountry[1]))});
splitCountry[0]
答案 5 :(得分:1)
这是我的两分钱。
const a = [
{ country: 'France', value: 100 },
{ country: 'France', value: 100 },
{ country: 'Romania', value: 500 },
{ country: 'England', value: 400 },
{ country: 'England', value: 400 },
{ country: 'Spain', value: 130 },
{ country: 'Albania', value: 4 },
{ country: 'Hungary', value: 3 }
];
const diff = (a, b) => b.value - a.value;
const addValues = (acc, {value}) => R.add(acc,value);
const count = R.reduce(addValues, 0);
const toCountry = ({country}) => country;
const toCountryObj = (x) => ({'country': x[0], 'value': x[1] });
const reduceC = R.reduceBy(addValues, [], toCountry);
const [countries, others] = R.compose(
R.splitAt(4),
R.sort(diff),
R.chain(toCountryObj),
R.toPairs,
reduceC)(a);
const othersArray = [{ 'country': 'Others', 'value': count(others) }];
R.concat(countries, othersArray);
答案 6 :(得分:1)
我将按国家/地区分组,将每个国家/地区组合并为一个对象,同时对值求和,排序,拆分为两个数组(最高4个)和[其他],将其他国家/地区合并为单个对象,并与最高4。
const { pipe, groupBy, prop, values, map, converge, merge, head, pluck, sum, objOf, sort, descend, splitAt, concat, last, of, assoc } = R
const sumProp = key => pipe(pluck(key), sum, objOf(key))
const combineProp = key => converge(merge, [head, sumProp(key)])
const getTop5 = pipe(
groupBy(prop('country')),
values, // convert to array of country arrays
map(combineProp('value')), // merge each sub array to a single object
sort(descend(prop('value'))), // sort descebdubg by the value property
splitAt(4), // split to two arrays [4 highest][the rest]
converge(concat, [ // combine the highest and the others object
head,
// combine the rest to the others object wrapped in an array
pipe(last, combineProp('value'), assoc('country', 'others'), of)
])
)
const countries = [{ country: 'France', value: 100 }, { country: 'France', value: 100 }, { country: 'Romania', value: 500 }, { country: 'England', value: 400 }, { country: 'England', value: 400 }, { country: 'Spain', value: 130 }, { country: 'Albania', value: 4 }, { country: 'Hungary', value: 3 }]
const result = getTop5(countries)
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>
答案 7 :(得分:1)
我可能会做这样的事情:
const aggregate = R.pipe(
R.groupBy(R.prop('country')),
R.toPairs,
R.map(
R.applySpec({
country: R.head,
value: R.pipe(R.last, R.pluck('value'), R.sum),
}),
),
R.sort(R.descend(R.prop('value'))),
R.splitAt(4),
R.over(
R.lensIndex(1),
R.applySpec({
country: R.always('Others'),
value: R.pipe(R.pluck('value'), R.sum),
}),
),
R.unnest,
);
const data = [
{ country: 'France', value: 100 },
{ country: 'France', value: 100 },
{ country: 'Romania', value: 500 },
{ country: 'England', value: 400 },
{ country: 'England', value: 400 },
{ country: 'Spain', value: 130 },
{ country: 'Albania', value: 4 },
{ country: 'Hungary', value: 3 }
];
console.log('result', aggregate(data));
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>
答案 8 :(得分:0)
我认为第二个更长的时间更容易理解
函数“ mergeAllWithKeyBy”结合了“ R.mergeAll”,“ R.mergeWithKey”和“ R.groupBy”的功能。
const mergeAllWithKeyBy = R.curry((mergeFn, keyFn, objs) =>
R.values(R.reduceBy(R.mergeWithKey(mergeFn), {}, keyFn, objs)))
const addValue = (k, l, r) =>
k === 'value' ? l + r : r
const getTop =
R.pipe(
mergeAllWithKeyBy(addValue, R.prop('country')),
R.sort(R.descend(R.prop('value'))),
R.splitAt(4),
R.adjust(-1, R.map(R.assoc('country', 'Others'))),
R.unnest,
mergeAllWithKeyBy(addValue, R.prop('country')),
)
const data = [
{ country: 'France', value: 100 },
{ country: 'France', value: 100 },
{ country: 'Romania', value: 500 },
{ country: 'England', value: 400 },
{ country: 'England', value: 400 },
{ country: 'Spain', value: 130 },
{ country: 'Albania', value: 4 },
{ country: 'Hungary', value: 3 }
]
console.log(getTop(data))
<script src="//cdn.jsdelivr.net/npm/ramda@latest/dist/ramda.min.js"></script>
const getTop = (data) => {
const getCountryValue =
R.prop(R.__, R.reduceBy((y, x) => y + x.value, 0, R.prop('country'), data))
const countries =
R.uniq(R.pluck('country', data))
const [topCounties, bottomCountries] =
R.splitAt(4, R.sort(R.descend(getCountryValue), countries))
const others = {
country: 'Others',
value: R.sum(R.map(getCountryValue, bottomCountries))
}
const top =
R.map(R.applySpec({country: R.identity, value: getCountryValue}), topCounties)
return R.append(others, top)
}
const data = [
{ country: 'France', value: 100 },
{ country: 'France', value: 100 },
{ country: 'Romania', value: 500 },
{ country: 'England', value: 400 },
{ country: 'England', value: 400 },
{ country: 'Spain', value: 130 },
{ country: 'Albania', value: 4 },
{ country: 'Hungary', value: 3 }
]
console.log(getTop(data))
<script src="//cdn.jsdelivr.net/npm/ramda@latest/dist/ramda.min.js"></script>