使用 Ramda/LoDash/Underscore 对嵌套属性进行分组和唯一计数
Group and Count Uniquely on Nested Properties with Ramda/LoDash/Underscore
如何对嵌套的 属性 进行分组和计数?如果这看起来是一个非常基本的问题,我深表歉意,但老实说,我什至不确定从哪里开始。
编辑 最初我对上面的描述太含糊了,这可能是因为我的英语不是很好。我将在这里进一步阐述。
如何按每个产品名称分组,然后 aggregate/count 唯一嵌套项的数量?
我的数据来源:
[
{
product_name: 'Cool Gadget',
offer_code: {name: '50off'}
},
{
product_name: 'Cool Gadget',
offer_code: {name: '50OFF'}
},
{
product_name: 'Cool Gadget',
offer_code: {name: '75OFF'}
},
{
product_name: 'Another Cool Gadget'
},
{
product_name: 'Another Cool Gadget',
offer_code: {name: '50OFF'}
},
{
product_name: 'Another Cool Gadget',
offer_code: {name: '50OFF'}
}
]
我的首选输出:
[
{
product_name: 'Cool Gadget',
count: {
'50OFF': 2,
'75OFF': 1
}
},
{
product_name: 'Another Cool Gadget',
count: {
'_default': 1,
'50OFF': 2
}
}
]
我们可以使用 Ramda 完成一种解决方案。
var data = [
{
product_name: 'Cool Gadget',
offer_code: {name: '50OFF'}
},
{
product_name: 'Cool Gadget',
offer_code: {name: '50OFF'}
},
{
product_name: 'Cool Gadget',
offer_code: {name: '75OFF'}
},
{
product_name: 'Another Cool Gadget'
},
{
product_name: 'Another Cool Gadget',
offer_code: {name: '50OFF'}
},
{
product_name: 'Another Cool Gadget',
offer_code: {name: '50OFF'}
}
];
我们将从创建一个函数开始,该函数将按名称对产品列表进行分组。
const groupByProductName = R.groupBy(R.prop('product_name'));
groupByProductName(data);
// {"Another Cool Gadget": [{"product_name": "Another Cool Gadget"}, {"offer_code": {"name": "50OFF"}, "product_name": "Another Cool Gadget"}, {"offer_code": {"name": "50OFF"}, "product_name": "Another Cool Gadget"}], "Cool Gadget": [{"offer_code": {"name": "50OFF"}, "product_name": "Cool Gadget"}, {"offer_code": {"name": "50OFF"}, "product_name": "Cool Gadget"}, {"offer_code": {"name": "75OFF"}, "product_name": "Cool Gadget"}]}
为了帮助计算不同优惠代码的数量,我们将创建一个新函数以根据优惠代码名称(如果存在)进行分组,如果不存在则默认为 _default
。
我们可以使用此函数映射由 groupByProductName
生成的对象中的值。
const groupByOfferCode = R.groupBy(R.pathOr('_default', ['offer_code', 'name']));
R.map(groupByOfferCode, groupByProductName(data));
// {"Another Cool Gadget": {"50OFF": [{"offer_code": {"name": "50OFF"}, "product_name": "Another Cool Gadget"}, {"offer_code": {"name": "50OFF"}, "product_name": "Another Cool Gadget"}], "_default": [{"product_name": "Another Cool Gadget"}]}, "Cool Gadget": {"50OFF": [{"offer_code": {"name": "50OFF"}, "product_name": "Cool Gadget"}, {"offer_code": {"name": "50OFF"}, "product_name": "Cool Gadget"}], "75OFF": [{"offer_code": {"name": "75OFF"}, "product_name": "Cool Gadget"}]}}
按名称对优惠代码进行分组后,我们将创建一个新函数,以仅使用每个数组的长度来交换代码数组。
const countOfferCodes = R.map(R.length);
R.map(countOfferCodes, R.map(groupByOfferCode, groupByProductName(data)));
// {"Another Cool Gadget": {"50OFF": 2, "_default": 1}, "Cool Gadget": {"50OFF": 2, "75OFF": 1}}
一旦我们定义了这些函数,我们就可以获得接近您想要的输出的结果。
const process = products => R.map(countOfferCodes, R.map(groupByOfferCode, groupByProductName(products)));
process(data);
// {"Another Cool Gadget": {"50OFF": 2, "_default": 1}, "Cool Gadget": {"50OFF": 2, "75OFF": 1}}
鉴于所有这些函数都将它们的输出直接提供给下一个函数的输入,这可以改为使用 R.pipe
来声明以创建转换管道。
const process = R.pipe(
groupByProductName,
R.map(groupByOfferCode),
R.map(countOfferCodes)
);
您可能已经注意到我们在管道中有两个 R.map
函数彼此相邻。由于法律规定 R.pipe(R.map(f), R.map(g))
必须与 R.map(R.pipe(f, g))
相同,我们可以通过将我们的管道修改为以下内容来防止循环列表两次。
const process = R.pipe(
groupByProductName,
R.map(R.pipe(
groupByOfferCode,
countOfferCodes
))
);
现在为了将输出变成所需的形状,我们可以创建一个函数将对象转换为列表,我们可以将其添加到管道的末尾。
const objToList = R.pipe(
R.toPairs,
R.map(R.zipObj(['product_name', 'count']))
);
最后,我们可以向管道添加一个函数以按产品名称排序。所以一起:
const groupByProductName = R.groupBy(R.prop('product_name'));
const groupByOfferCode = R.groupBy(R.pathOr('_default', ['offer_code', 'name']));
const countOfferCodes = R.map(R.length);
const objToList = R.pipe(
R.toPairs,
R.map(R.zipObj(['product_name', 'count']))
);
const process = R.pipe(
groupByProductName,
R.map(R.pipe(
groupByOfferCode,
countOfferCodes
)),
objToList,
R.sortBy(R.prop('product_name'))
);
process(data);
// [{"count": {"50OFF": 2, "_default": 1}, "product_name": "Another Cool Gadget"}, {"count": {"50OFF": 2, "75OFF": 1}, "product_name": "Cool Gadget"}]
我们完成了。
如何对嵌套的 属性 进行分组和计数?如果这看起来是一个非常基本的问题,我深表歉意,但老实说,我什至不确定从哪里开始。
编辑 最初我对上面的描述太含糊了,这可能是因为我的英语不是很好。我将在这里进一步阐述。
如何按每个产品名称分组,然后 aggregate/count 唯一嵌套项的数量?
我的数据来源:
[
{
product_name: 'Cool Gadget',
offer_code: {name: '50off'}
},
{
product_name: 'Cool Gadget',
offer_code: {name: '50OFF'}
},
{
product_name: 'Cool Gadget',
offer_code: {name: '75OFF'}
},
{
product_name: 'Another Cool Gadget'
},
{
product_name: 'Another Cool Gadget',
offer_code: {name: '50OFF'}
},
{
product_name: 'Another Cool Gadget',
offer_code: {name: '50OFF'}
}
]
我的首选输出:
[
{
product_name: 'Cool Gadget',
count: {
'50OFF': 2,
'75OFF': 1
}
},
{
product_name: 'Another Cool Gadget',
count: {
'_default': 1,
'50OFF': 2
}
}
]
我们可以使用 Ramda 完成一种解决方案。
var data = [ { product_name: 'Cool Gadget', offer_code: {name: '50OFF'} }, { product_name: 'Cool Gadget', offer_code: {name: '50OFF'} }, { product_name: 'Cool Gadget', offer_code: {name: '75OFF'} }, { product_name: 'Another Cool Gadget' }, { product_name: 'Another Cool Gadget', offer_code: {name: '50OFF'} }, { product_name: 'Another Cool Gadget', offer_code: {name: '50OFF'} } ];
我们将从创建一个函数开始,该函数将按名称对产品列表进行分组。
const groupByProductName = R.groupBy(R.prop('product_name')); groupByProductName(data); // {"Another Cool Gadget": [{"product_name": "Another Cool Gadget"}, {"offer_code": {"name": "50OFF"}, "product_name": "Another Cool Gadget"}, {"offer_code": {"name": "50OFF"}, "product_name": "Another Cool Gadget"}], "Cool Gadget": [{"offer_code": {"name": "50OFF"}, "product_name": "Cool Gadget"}, {"offer_code": {"name": "50OFF"}, "product_name": "Cool Gadget"}, {"offer_code": {"name": "75OFF"}, "product_name": "Cool Gadget"}]}
为了帮助计算不同优惠代码的数量,我们将创建一个新函数以根据优惠代码名称(如果存在)进行分组,如果不存在则默认为 _default
。
我们可以使用此函数映射由 groupByProductName
生成的对象中的值。
const groupByOfferCode = R.groupBy(R.pathOr('_default', ['offer_code', 'name'])); R.map(groupByOfferCode, groupByProductName(data)); // {"Another Cool Gadget": {"50OFF": [{"offer_code": {"name": "50OFF"}, "product_name": "Another Cool Gadget"}, {"offer_code": {"name": "50OFF"}, "product_name": "Another Cool Gadget"}], "_default": [{"product_name": "Another Cool Gadget"}]}, "Cool Gadget": {"50OFF": [{"offer_code": {"name": "50OFF"}, "product_name": "Cool Gadget"}, {"offer_code": {"name": "50OFF"}, "product_name": "Cool Gadget"}], "75OFF": [{"offer_code": {"name": "75OFF"}, "product_name": "Cool Gadget"}]}}
按名称对优惠代码进行分组后,我们将创建一个新函数,以仅使用每个数组的长度来交换代码数组。
const countOfferCodes = R.map(R.length); R.map(countOfferCodes, R.map(groupByOfferCode, groupByProductName(data))); // {"Another Cool Gadget": {"50OFF": 2, "_default": 1}, "Cool Gadget": {"50OFF": 2, "75OFF": 1}}
一旦我们定义了这些函数,我们就可以获得接近您想要的输出的结果。
const process = products => R.map(countOfferCodes, R.map(groupByOfferCode, groupByProductName(products))); process(data); // {"Another Cool Gadget": {"50OFF": 2, "_default": 1}, "Cool Gadget": {"50OFF": 2, "75OFF": 1}}
鉴于所有这些函数都将它们的输出直接提供给下一个函数的输入,这可以改为使用 R.pipe
来声明以创建转换管道。
const process = R.pipe( groupByProductName, R.map(groupByOfferCode), R.map(countOfferCodes) );
您可能已经注意到我们在管道中有两个 R.map
函数彼此相邻。由于法律规定 R.pipe(R.map(f), R.map(g))
必须与 R.map(R.pipe(f, g))
相同,我们可以通过将我们的管道修改为以下内容来防止循环列表两次。
const process = R.pipe( groupByProductName, R.map(R.pipe( groupByOfferCode, countOfferCodes )) );
现在为了将输出变成所需的形状,我们可以创建一个函数将对象转换为列表,我们可以将其添加到管道的末尾。
const objToList = R.pipe( R.toPairs, R.map(R.zipObj(['product_name', 'count'])) );
最后,我们可以向管道添加一个函数以按产品名称排序。所以一起:
const groupByProductName = R.groupBy(R.prop('product_name')); const groupByOfferCode = R.groupBy(R.pathOr('_default', ['offer_code', 'name'])); const countOfferCodes = R.map(R.length); const objToList = R.pipe( R.toPairs, R.map(R.zipObj(['product_name', 'count'])) ); const process = R.pipe( groupByProductName, R.map(R.pipe( groupByOfferCode, countOfferCodes )), objToList, R.sortBy(R.prop('product_name')) ); process(data); // [{"count": {"50OFF": 2, "_default": 1}, "product_name": "Another Cool Gadget"}, {"count": {"50OFF": 2, "75OFF": 1}, "product_name": "Cool Gadget"}]
我们完成了。