按属性

时间:2015-10-02 22:52:24

标签: javascript arrays sorting properties

我刚刚开始严肃对待编码。 :) 我遇到了一个对我来说太复杂的问题。

如何按促销类型对以下产品进行分组?

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;
}

4 个答案:

答案 0 :(得分:1)

解释

您可以编写一个循环遍历数组的函数,并在指定的属性中搜索唯一值。在处理简单数据类型时很容易做到这一点,但可以使用帮助器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

准备的示例

答案 1 :(得分:1)

欢迎来到编码世界。许多人通过尝试编写一些代码开始出现问题,然后他们想知道为什么它不起作用并且不知所措,不知道调试它的基础知识,然后在这里发布到SO。他们错过了编程的关键第一步,即弄清楚你将如何去做。这也称为设计算法。算法通常使用称为伪代码的东西来描述。它的优点是可以查看,理解和建立它以做正确的事情,而不会陷入编程语言的所有平凡细节中。

一些非常聪明的人会想出一些算法 - 比如用于字符串匹配的Boyer-Moore算法 - 然后还有其他算法,程序员每天都会将其作为工作的一部分。

SO的问题在于,经常会有人发布一个基本上与算法相关的问题,然后所有键盘快乐的代码骑手都会突然发现它并提出一个代码片段,在很多情况下它是如此扭曲和钝化人们甚至无法看到底层算法是什么。

您提出的解决问题的算法是什么?你可以发布这个,人们可能会给你合理的评论,和/或如果你也给出了一个因某些原因不起作用的实际实现,请帮助你理解你出错的地方。

冒着掠夺你设计自己的算法来解决这个问题的风险,这是一个例子:

  1. 为结果创建一个空数组。

  2. 循环输入中的产品。

  3. 对于每种产品,请循环推广。

  4. 在结果数组中查找促销。

  5. 如果结果数组中没有此类促销,请创建一个新的产品列表。

  6. 将产品添加到阵列中促销条目中的产品数组中。

  7. 在伪代码中:

    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);
    

    换句话说,为processProduct(产品列表)的每个元素调用data函数。只要`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;
    }
    

答案 2 :(得分:0)

好的,经过几个小时的工作,在网上和线下都有很多帮助,我终于成功了。感谢帮助过的人。

如果您有更优雅的解决方案,请发表评论,总是喜欢学习。

遇到类似问题的人:

这是我的解决方案

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;
}

答案 3 :(得分:-1)

查看lodash - 一个可以进行各种转换的漂亮库。

lodash.groupBy正是您要找的。