按 属性 对数组中的对象进行分组
Group objects in array by property
我刚刚开始认真学习编码。 :)
我遇到了一个对我来说似乎太复杂的问题。
如何按促销类型对以下产品进行分组?
var data = [
{
name:'product1',
price:'40',
promotion:[
{
name:'Buy 3 get 30% off',
code:'ewq123'
},
{
name:'Free Gift',
code:'abc140'
}
]
},
{
name:'product2',
price:'40',
promotion:[
{
name:'Buy 3 get 30% off',
code:'ewq123'
}
]
},
{
name:'product3',
price:'40',
promotion:[
{
name:'Buy 3 get 30% off',
code:'ewq123'
}
]
},
{
name:'product4',
price:'40'
},
{
name:'product5',
price:'40',
promotion:[
{name:'30% off', code:'fnj245'}
]
},
{
name:'product6',
price:'0',
promotion:[
{
name:'Free Gift',
code:'abc140'
}
]
}
];
我想得到以下格式的结果
result =[
{
name : 'Buy 3 get 30% off',
code: 'ewq123',
products: [
... array of products
]
},
{
name : '30% off',
code: 'fnj245',
products: [
... array of products
]
},
{
...
}
];
我可以通过促销代码获取产品列表,但如何才能使其通用?
function productHasPromo(product, promotion){
if(!product.hasOwnProperty('promotion')) return false;
var productPromo = product.promotion;
for(var i=0; i<productPromo.length; i++){
if(productPromo[i].code === promotion){
return true;
}
}
return false;
}
function groupProductByPromo(products, promotion){
var arr = [];
for(var i=0; i<products.length; i++){
if(productHasPromo(products[i], promotion)){
arr.push(products[i]);
}
}
return arr;
}
查看 lodash - 一个用于执行各种转换的漂亮库。
lodash.groupBy
就是您要找的。
说明
您可以编写一个循环遍历数组的函数,并在指定的 属性 中搜索 唯一值 。这在处理简单数据类型时很容易完成,但可以使用辅助函数 grouping
函数来处理更复杂的结构作为对象数组(如您的示例)。
由于您还需要在分组后以特定格式输出,因此我们还必须处理 transformer
。此转换器将接收原始数据和 grouping
函数提取的 唯一值 ,并将生成所需的输出。
示例中使用了以下函数:
Array.prototype.groupBy = function (property, grouping, transformer) {
var values = [];
this.forEach(function (item) {
grouping.call(this, item, property).forEach(function (item) {
if (!values.contains(property, item[property])) {
values.push(item);
}
});
});
return transformer.call(this, values);
};
Array.prototype.contains = function (key, value) {
return this.find(function (elm) {
return elm[key] === value;
});
};
function transformerFunction(values) {
this.forEach(function (item) {
if (!item.promotion) return;
item.promotion.forEach(function (promotion) {
values.forEach(function (option) {
if (option.code === promotion.code) {
if (option.products) {
option.products.push(item);
} else {
option.products = [item];
}
}
});
});
});
return values;
}
function groupingFunction(item, property) {
if (!item.promotion) return [];
var values = [];
item.promotion.forEach(function (promotion) {
if (!values.contains(property, promotion[property])) {
values.push(promotion);
}
});
return values;
}
用法如下:
var items = data.groupBy('code', groupFunction, transformFunction);
例子
查看我在 jsfiddle
准备的示例
欢迎来到编码世界。很多人从尝试编写一些代码开始遇到问题,然后他们想知道为什么它不起作用并挠头,不知道调试它的基础知识,然后 post 到这里.他们错过了编程中关键的第一步,即弄清楚你将如何去做。这也称为设计算法。通常使用称为 伪代码 的东西来描述算法。它的优点是可以查看、理解并确定它可以做正确的事情,而不会陷入编程语言的所有世俗细节中。
有一些算法是由一些非常聪明的人想出的——比如用于字符串匹配的 Boyer-Moore 算法——还有其他算法是程序员每天在工作中设计的。
SO 的问题在于,经常有人 post 提出一个本质上是关于算法的问题,然后所有喜欢键盘的代码骑师都会扑向它并想出一个代码片段,在许多情况下案例是如此扭曲和迟钝,以至于人们甚至看不到底层算法是什么。
你提出的解决问题的算法是什么?你可以 post 那样,人们可能会给你合理的评论,and/or 如果你还提供了一个由于某种原因不起作用的实际实现,帮助你了解你哪里出错了。
冒着剥夺你设计自己的算法来解决这个问题的乐趣的风险,这里有一个例子:
为结果创建一个空数组。
循环输入中的产品。
对于每个产品,循环查看其促销活动。
在结果数组中查找促销。
如果结果数组中没有这样的促销,创建一个新的,产品列表为空。
将产品添加到数组中促销入口的产品数组中。
在伪代码中:
results = new Array // 1
for each product in products (data) // 2
for each promotion in promotions field of product // 3
if results does not contain promotion by that name // 4
add promotion to results, with empty products field // 5
add product to products field of results.promotion // 6
如果我们认为这是正确的,我们现在可以尝试将其写入 JavaScript。
var result = []; // 1
for (var i = 0; i < data.length; i++) { // 2
var product = data[i];
var promotions = product.promotion;
for (var j = 0; j < promotions.length; j++) { // 3
var promotion = promotions[i];
var name = promotion.name;
var result_promotion = find_promotion_by_name(name);
if (!result_promotion) { // 4
result_promotion = { name: name, products: [], code: promotion.code };
result.push(result_promotion); // 5
}
result_promotion.products.push(name); // 6
}
}
这段代码没问题,应该可以完成工作(未经测试)。但是,它仍然有点不可读。它并没有非常严格地遵循伪代码。它不知何故仍然隐藏了算法。很难确定它是完全正确的。所以,我们想重写它。 Array#foreach
之类的函数可以更容易地做到这一点。顶层可以简单地是:
var result = [];
data.forEach(processProduct);
换句话说,为 data
(产品列表)的每个元素调用 processProduct
函数。只要 `processProduct 实现不正确,这段代码就很难 出错。
function processProduct(product) {
product.promotion.forEach(processPromotion);
}
同样,假设 processPromotion
已正确实施,此逻辑可证明是正确的。
function processPromotion(promotion) {
var result_promotion = getPromotionInResults(promotion);
result_promotion.products.push(name);
}
这再清楚不过了。我们在结果数组中获取此促销的条目,然后将产品添加到其产品列表中。
现在我们需要简单地实现getPromotionInResults
。如果促销元素不存在,这将包括在结果数组中创建促销元素的逻辑。
function getPromotionInResults(promotion) {
var promotionInResults = findPromotionInResultsByName(promotion.name);
if (!promotionInResults) {
promotionInResults = {name: promotion.name, code: promotion.code, products: []};
result.push(promotionInResults);
}
return promotionInResults;
}
这似乎也很正确。但是我们还是要实现findPromotionInResultsByName
。为此,我们可以使用 Array#find
,或一些等效的库例程或 polyfill:
function findPromotionInResultsByName(name) {
return result.find(function(promotion) {
return promotion.name === name;
});
}
整个解决方案就是这样
function transform(data) {
// Given a product, update the result accordingly.
function processProduct(product) {
product.promotion.forEach(processPromotion);
}
// Given a promotion, update its list of products in results.
function processPromotion(promotion) {
var result_promotion = getPromotionInResults(promotion);
result_promotion.products.push(name);
}
// Find or create the promotion entries in results.
function getPromotionInResults(promotion) {
var promotionInResults = findPromotionInResultsByName(promotion.name);
if (!promotionInResults) {
promotionInResults = {name: promotion.name, code: promotion.code, products: []};
result.push(promotionInResults);
}
return promotionInResults;
}
// Find an existing entry in results, by its name.
function findPromotionInResultsByName(name) {
return result.find(function(promotion) {
return promotion.name === name;
});
}
var result = [];
data.forEach(processProduct);
return result;
}
好的,经过几个小时的努力,在网上和线下的大量帮助下,我终于成功了。感谢帮助过的人。
如果有更优雅的解决方案欢迎评论,永远爱学习。
对于 运行 遇到类似问题的人:
这是我的解决方案
function groupProductsByPromo(data){
var result = [];
// filter only product with promotion
var productsWithPromo = data.filter(function(product){
return product.hasOwnProperty('promotions');
});
// create promotions map
var mappedProducts = productsWithPromo.map(function(product) {
var mapping = {};
product.promotions.forEach(function(promotion) {
mapping[promotion.code] = {
promotion: promotion
};
});
return mapping;
});
// reduce duplicates in promotion map
mappedProducts = mappedProducts.reduce(function(flattenObject, mappedProducts) {
for (var promoCode in mappedProducts) {
if (flattenObject.hasOwnProperty(promoCode)) {
continue;
}
flattenObject[promoCode] = {
code: promoCode,
name: mappedProducts[promoCode].promotion.name
};
}
return flattenObject;
}, {});
// add products to promo item
for(var promoCode in mappedProducts){
mappedProducts[promoCode].products = productsWithPromo.filter(function(product){
return product.promotions.some(function(promo){
return promo.code === promoCode;
});
});
result.push(mappedProducts[promoCode]);
}
return result;
}
我刚刚开始认真学习编码。 :) 我遇到了一个对我来说似乎太复杂的问题。
如何按促销类型对以下产品进行分组?
var data = [
{
name:'product1',
price:'40',
promotion:[
{
name:'Buy 3 get 30% off',
code:'ewq123'
},
{
name:'Free Gift',
code:'abc140'
}
]
},
{
name:'product2',
price:'40',
promotion:[
{
name:'Buy 3 get 30% off',
code:'ewq123'
}
]
},
{
name:'product3',
price:'40',
promotion:[
{
name:'Buy 3 get 30% off',
code:'ewq123'
}
]
},
{
name:'product4',
price:'40'
},
{
name:'product5',
price:'40',
promotion:[
{name:'30% off', code:'fnj245'}
]
},
{
name:'product6',
price:'0',
promotion:[
{
name:'Free Gift',
code:'abc140'
}
]
}
];
我想得到以下格式的结果
result =[
{
name : 'Buy 3 get 30% off',
code: 'ewq123',
products: [
... array of products
]
},
{
name : '30% off',
code: 'fnj245',
products: [
... array of products
]
},
{
...
}
];
我可以通过促销代码获取产品列表,但如何才能使其通用?
function productHasPromo(product, promotion){
if(!product.hasOwnProperty('promotion')) return false;
var productPromo = product.promotion;
for(var i=0; i<productPromo.length; i++){
if(productPromo[i].code === promotion){
return true;
}
}
return false;
}
function groupProductByPromo(products, promotion){
var arr = [];
for(var i=0; i<products.length; i++){
if(productHasPromo(products[i], promotion)){
arr.push(products[i]);
}
}
return arr;
}
查看 lodash - 一个用于执行各种转换的漂亮库。
lodash.groupBy
就是您要找的。
说明
您可以编写一个循环遍历数组的函数,并在指定的 属性 中搜索 唯一值 。这在处理简单数据类型时很容易完成,但可以使用辅助函数 grouping
函数来处理更复杂的结构作为对象数组(如您的示例)。
由于您还需要在分组后以特定格式输出,因此我们还必须处理 transformer
。此转换器将接收原始数据和 grouping
函数提取的 唯一值 ,并将生成所需的输出。
示例中使用了以下函数:
Array.prototype.groupBy = function (property, grouping, transformer) {
var values = [];
this.forEach(function (item) {
grouping.call(this, item, property).forEach(function (item) {
if (!values.contains(property, item[property])) {
values.push(item);
}
});
});
return transformer.call(this, values);
};
Array.prototype.contains = function (key, value) {
return this.find(function (elm) {
return elm[key] === value;
});
};
function transformerFunction(values) {
this.forEach(function (item) {
if (!item.promotion) return;
item.promotion.forEach(function (promotion) {
values.forEach(function (option) {
if (option.code === promotion.code) {
if (option.products) {
option.products.push(item);
} else {
option.products = [item];
}
}
});
});
});
return values;
}
function groupingFunction(item, property) {
if (!item.promotion) return [];
var values = [];
item.promotion.forEach(function (promotion) {
if (!values.contains(property, promotion[property])) {
values.push(promotion);
}
});
return values;
}
用法如下:
var items = data.groupBy('code', groupFunction, transformFunction);
例子
查看我在 jsfiddle
准备的示例欢迎来到编码世界。很多人从尝试编写一些代码开始遇到问题,然后他们想知道为什么它不起作用并挠头,不知道调试它的基础知识,然后 post 到这里.他们错过了编程中关键的第一步,即弄清楚你将如何去做。这也称为设计算法。通常使用称为 伪代码 的东西来描述算法。它的优点是可以查看、理解并确定它可以做正确的事情,而不会陷入编程语言的所有世俗细节中。
有一些算法是由一些非常聪明的人想出的——比如用于字符串匹配的 Boyer-Moore 算法——还有其他算法是程序员每天在工作中设计的。
SO 的问题在于,经常有人 post 提出一个本质上是关于算法的问题,然后所有喜欢键盘的代码骑师都会扑向它并想出一个代码片段,在许多情况下案例是如此扭曲和迟钝,以至于人们甚至看不到底层算法是什么。
你提出的解决问题的算法是什么?你可以 post 那样,人们可能会给你合理的评论,and/or 如果你还提供了一个由于某种原因不起作用的实际实现,帮助你了解你哪里出错了。
冒着剥夺你设计自己的算法来解决这个问题的乐趣的风险,这里有一个例子:
为结果创建一个空数组。
循环输入中的产品。
对于每个产品,循环查看其促销活动。
在结果数组中查找促销。
如果结果数组中没有这样的促销,创建一个新的,产品列表为空。
将产品添加到数组中促销入口的产品数组中。
在伪代码中:
results = new Array // 1
for each product in products (data) // 2
for each promotion in promotions field of product // 3
if results does not contain promotion by that name // 4
add promotion to results, with empty products field // 5
add product to products field of results.promotion // 6
如果我们认为这是正确的,我们现在可以尝试将其写入 JavaScript。
var result = []; // 1
for (var i = 0; i < data.length; i++) { // 2
var product = data[i];
var promotions = product.promotion;
for (var j = 0; j < promotions.length; j++) { // 3
var promotion = promotions[i];
var name = promotion.name;
var result_promotion = find_promotion_by_name(name);
if (!result_promotion) { // 4
result_promotion = { name: name, products: [], code: promotion.code };
result.push(result_promotion); // 5
}
result_promotion.products.push(name); // 6
}
}
这段代码没问题,应该可以完成工作(未经测试)。但是,它仍然有点不可读。它并没有非常严格地遵循伪代码。它不知何故仍然隐藏了算法。很难确定它是完全正确的。所以,我们想重写它。 Array#foreach
之类的函数可以更容易地做到这一点。顶层可以简单地是:
var result = [];
data.forEach(processProduct);
换句话说,为 data
(产品列表)的每个元素调用 processProduct
函数。只要 `processProduct 实现不正确,这段代码就很难 出错。
function processProduct(product) {
product.promotion.forEach(processPromotion);
}
同样,假设 processPromotion
已正确实施,此逻辑可证明是正确的。
function processPromotion(promotion) {
var result_promotion = getPromotionInResults(promotion);
result_promotion.products.push(name);
}
这再清楚不过了。我们在结果数组中获取此促销的条目,然后将产品添加到其产品列表中。
现在我们需要简单地实现getPromotionInResults
。如果促销元素不存在,这将包括在结果数组中创建促销元素的逻辑。
function getPromotionInResults(promotion) {
var promotionInResults = findPromotionInResultsByName(promotion.name);
if (!promotionInResults) {
promotionInResults = {name: promotion.name, code: promotion.code, products: []};
result.push(promotionInResults);
}
return promotionInResults;
}
这似乎也很正确。但是我们还是要实现findPromotionInResultsByName
。为此,我们可以使用 Array#find
,或一些等效的库例程或 polyfill:
function findPromotionInResultsByName(name) {
return result.find(function(promotion) {
return promotion.name === name;
});
}
整个解决方案就是这样
function transform(data) {
// Given a product, update the result accordingly.
function processProduct(product) {
product.promotion.forEach(processPromotion);
}
// Given a promotion, update its list of products in results.
function processPromotion(promotion) {
var result_promotion = getPromotionInResults(promotion);
result_promotion.products.push(name);
}
// Find or create the promotion entries in results.
function getPromotionInResults(promotion) {
var promotionInResults = findPromotionInResultsByName(promotion.name);
if (!promotionInResults) {
promotionInResults = {name: promotion.name, code: promotion.code, products: []};
result.push(promotionInResults);
}
return promotionInResults;
}
// Find an existing entry in results, by its name.
function findPromotionInResultsByName(name) {
return result.find(function(promotion) {
return promotion.name === name;
});
}
var result = [];
data.forEach(processProduct);
return result;
}
好的,经过几个小时的努力,在网上和线下的大量帮助下,我终于成功了。感谢帮助过的人。
如果有更优雅的解决方案欢迎评论,永远爱学习。
对于 运行 遇到类似问题的人:
这是我的解决方案
function groupProductsByPromo(data){
var result = [];
// filter only product with promotion
var productsWithPromo = data.filter(function(product){
return product.hasOwnProperty('promotions');
});
// create promotions map
var mappedProducts = productsWithPromo.map(function(product) {
var mapping = {};
product.promotions.forEach(function(promotion) {
mapping[promotion.code] = {
promotion: promotion
};
});
return mapping;
});
// reduce duplicates in promotion map
mappedProducts = mappedProducts.reduce(function(flattenObject, mappedProducts) {
for (var promoCode in mappedProducts) {
if (flattenObject.hasOwnProperty(promoCode)) {
continue;
}
flattenObject[promoCode] = {
code: promoCode,
name: mappedProducts[promoCode].promotion.name
};
}
return flattenObject;
}, {});
// add products to promo item
for(var promoCode in mappedProducts){
mappedProducts[promoCode].products = productsWithPromo.filter(function(product){
return product.promotions.some(function(promo){
return promo.code === promoCode;
});
});
result.push(mappedProducts[promoCode]);
}
return result;
}