【问题标题】:Group and aggregate an array of javascript objects对一组 javascript 对象进行分组和聚合
【发布时间】:2017-02-14 14:03:18
【问题描述】:

我在合理化和聚合 javascript 中的对象数组时遇到了一些麻烦。

给定数组

[{"description":"Bright","size":"2XL","price":10.99},{"description":"Bright","size":"XL","price":10.99},{"description":"Bright","size":"L","price":9.99},{"group":"Foos","description":"Dull","size":"XL","price":9.99},{"description":"Dull","size":"L","price":8.99},{"description":"Dull","size":"2XL","price":9.99},{"description":"Shiny","size":"XL","price":9.99},{"description":"Shiny","size":"S","price":8.99},{"description":"Shiny","size":"3XL","price":10.3},{"description":"Shiny","size":"2XL","price":9.99}]

我正在尝试将其转换为格式的数组(此处的实际值可能有误)。

[{"descriptions":"Shiny, Bright, Dull","sizeRange":"S - L","price":8.99},{"descriptions":"Shiny, Bright, Dull","sizes":"XL - 2XL","price":9.99},{"descriptions":"Dark","sizes":"S - 2XL","price":10.99}]

也就是说 - 我希望按价格对每组商品进行分组,显示它们的描述和尺寸范围。

到目前为止,这就是我所拥有的,而且它似乎正在工作,但它看起来很麻烦。如果使用 lodash 或 underscore 之类的东西有助于合理化代码而不是使用原生 JS,我真的会非常乐意。

function groupBy (array, key) {
  return array.reduce(function(value, property) {
    (value[property[key]] = value[property[key]] || []).push(property);
    return value;
  }, {});
};

function unique(array) {
  return Array.from(new Set(array));
};

function getRanges(data)
{
    var result = [];

    // simple map of sizes from smallest to largest to use for sorting
    var sizeSort = {'S':1, 'M':2, 'L':3, 'XL':4, '2XL':5, '3XL':6, '4XL':7, '5XL':8};

    // group the remaining variants by price
    var group = groupBy(data, 'price');

    // for each variant price group 
    for(var price in group) {
        var item = {};
        item.price = price;
        // get the range of sizes sorted smallest to largest
        var sizes = unique(group[price].map(function(i) {
            return i.size;
        })).sort(function(a, b) {
            return sizeSort[a] - sizeSort[b];
        });

        // Add single size, or first and last size.
        item.sizes = (sizes.length === 1) ?
           sizes.shift() :
           sizes.shift() + ' - ' + sizes.pop();

        // Add the descriptions as alphabetically sorted CSV
        item.description = unique(group[price].map(function(i) {
            return i.description;
        })).sort().join(", ");

        result.push(item);
    }

    return result;
}

【问题讨论】:

    标签: javascript arrays sorting underscore.js lodash


    【解决方案1】:

    这是一个使用 lodash 的版本.. 我觉得它看起来更理性..

    function calc(data) {
      var sizeSort = {'S':1, 'M':2, 'L':3, 'XL':4, '2XL':5, 
                      '3XL':6, '4XL':7, '5XL':8};
      return _.chain(data).
      groupBy('price').
      map(function(f){ 
         var sizes = _.chain(f).map('size').uniq().
            sortBy(function (a) { return sizeSort[a] }).value();
         return { 
           price: _.head(f).price,
           description: _.chain(f).map('description').uniq().join(',').value(),
           size: sizes.length === 1 ? _.first(sizes) : _.join([_.first(sizes),_.last(sizes)], ' - ')
         } 
      }).
      sortBy(['price']). 
      value();
    }
    
    //put data at end, so not having to scroll down to see code
    var data = [{"description":"Bright","size":"2XL","price":10.99},{"description":"Bright","size":"XL","price":10.99},{"description":"Bright","size":"L","price":9.99},{"group":"Foos","description":"Dull","size":"XL","price":9.99},{"description":"Dull","size":"L","price":8.99},{"description":"Dull","size":"2XL","price":9.99},{"description":"Shiny","size":"XL","price":9.99},{"description":"Shiny","size":"S","price":8.99},{"description":"Shiny","size":"3XL","price":10.3},{"description":"Shiny","size":"2XL","price":9.99}];
    
    console.log(calc(data));
    <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.16.3/lodash.js"></script>

    【讨论】:

    • 谢谢,这太棒了,lodash 链功能只是票,它确实使代码更清晰。
    【解决方案2】:

    一个普通的 JavaScript 解决方案(带有 ES6 模板字符串)

    /*
      Some boilerplate functions. Listed underscore/lodash functions that 
      could replace them above
    */
    
    // _.mapObject(object, reducer)
    function reduceValues(object, reducer) {
      let newObject = {}
        for (var property in object) {
          if (object.hasOwnProperty(property)) {
              newObject[property] = reducer(object[property])
          }
        }
      return newObject
    }
    
    // _.groupBy
    function groupBy(arr, key) {
      let reducer = (grouped, item) => {
        let group_value = item[key]
        if (!grouped[group_value]) {
          grouped[group_value] = []
        }
        grouped[group_value].push(item)
        return grouped
      }
      return arr.reduce(reducer, {})
    }
    
    // _.values
    function objectValues(object) {
      let values = []
      for (var property in object) {
        if (object.hasOwnProperty(property)) {
            values.push(object[property])
        }
      }
      return values
    }
    
    /*
      Shirt specific functions and data
    */
    
    // Mapping of shirts to their order.
    let sizesToNumbers = {'S':1, 'M':2, 'L':3, 'XL':4, '2XL':5, '3XL':6, '4XL':7, '5XL':8};
    
    // Create an intermediate summary with data instead of strings.
    // This makes processing easier to write and reason about
    function reduceShirtsToSummary(shirts) {
      let reducer = (summary, shirt) => {
        summary['descriptions'].add(shirt['description'])
        let shirtSize = shirt['size']
        if (!summary['smallestSize'] || sizesToNumbers[shirtSize] < sizesToNumbers[summary['smallestSize']]) {
          summary['smallestSize'] = shirtSize
        }
        if (!summary['largestSize'] || sizesToNumbers[shirtSize] > sizesToNumbers[summary['largestSize']]) {
          summary['largestSize'] = shirtSize
        }
        summary['prices'].push(shirt['price'])
        return summary
      }
      return shirts.reduce(reducer, {'descriptions': new Set(), 'prices': []})
    }
    
    // Convert the shirt summary data into the "labelized" version with strings in the example
    function labelizeShirtSummary(shirtSummary) {
      let labelizedShirtSummary = {}
      labelizedShirtSummary['descriptions'] = Array.from(shirtSummary['descriptions']).join(', ')
      labelizedShirtSummary['price'] = shirtSummary['prices'][0]
      labelizedShirtSummary['sizes'] = `${shirtSummary['smallestSize']} - ${shirtSummary['largestSize']}`
      return labelizedShirtSummary
    }
    
    let grouped = groupBy(shirts, 'price')
    let groupedAndSummarized = reduceValues(grouped, reduceShirtsToSummary)
    let labelizedSummaries = objectValues(groupedAndSummarized).map(labelizeShirtSummary)
    // Gives desired output
    console.log(labelizedSummaries)
    

    【讨论】:

    • 谢谢,FWIW 这不是作业问题 - 但如果是这样,问题会是什么?在这里对这些事情进行一些很好的讨论meta.stackoverflow.com/questions/334822/…
    • 抱歉,结束了漫长的一天。编辑以删除“似乎是作业问题”评论。
    猜你喜欢
    • 1970-01-01
    • 2018-03-19
    • 1970-01-01
    • 2021-11-21
    • 1970-01-01
    • 1970-01-01
    • 2022-12-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多