【问题标题】:JavaScript Map / Reduce to return grouped by countJavaScript Map / Reduce 按计数分组返回
【发布时间】:2017-03-16 05:50:49
【问题描述】:

我有一个 JSON 集合作为数组。我想按集合中的三个字段分组,然后返回结果以及匹配文档的数量。下面的示例有望使其更清晰。

返回的 JSON 文档集合:

[
    {
        _id: 1,
       browser: 'chrome',
       ipAddress: '222.111.111.0',
       uri: 'example1.com'
    },
    {
       _id: 2,
       browser: 'chrome',
       ipAddress: '222.111.111.0',
       uri: 'example1.com'
    },
    {
       _id: 3,
       browser: 'opera',
       ipAddress: '222.0.888.0',
       uri: 'example1.com'
    },
    {
       _id: 4,
       browser: 'chrome',
       ipAddress: '222.111.222.0',
       uri: 'sample1.com'
    },
    {
       _id: 5,
       browser: 'chrome',
       ipAddress: '222.111.222.0',
       uri: 'sample1.com'
    },
    {
       _id: 6,
       browser: 'chrome',
       ipAddress: '222.111.222.0',
       uri: 'sample1.com'
    },
    {
       _id: 7,
       browser: 'opera',
       ipAddress: '222.111.222.0',
       uri: 'sample1.com'
    }
]

应该对浏览器、ipAddress 和 uri 进行分组,然后返回分组结果以及下面的计数(我检查了几次,所以我希望下面的数字加起来就是上面每个组合的实例!)。

[
    {
       browser: 'chrome',
       ipAddress: '222.111.111.0',
       uri: 'example1.com',
       count: 2
    },
    {
       browser: 'opera',
       ipAddress: '222.0.888.0',
       uri: 'example1.com',
       count: 1
    },
    {
       browser: 'chrome',
       ipAddress: '222.111.222.0',
       uri: 'sample1.com',
       count: 3
    },
       browser: 'opera',
       ipAddress: '222.111.222.0',
       uri: 'sample1.com',
       count: 1
]

我知道使用 map/reduce 应该很容易做到这一点,但我似乎无法让我的大脑困惑如何做到这一点!

提前致谢。

【问题讨论】:

    标签: javascript json node.js mapreduce reduce


    【解决方案1】:

    使用lodash 的另一种(更实用的)方法:

    _(array).groupBy(v => ([v.browser, v.ipAddress, v.uri]))
            .map(v => _.merge(_.omit(v[0], '_id'), {count: v.length}))
            .value();
    

    简短说明:groupBy 使用浏览器、ipAddress 和 uri 创建分组。在map 语句中,我们删除了 _id 字段并根据组中对象的数量添加计数。

    【讨论】:

    • 谢谢莫里茨。根据 RaR,不是针对库,但在这种情况下,我想弄清楚如何仅使用 JS 运行它。
    • 与 Rajesh 的回答一样,它确实给了我正确的结果,将相同的记录组合在一起,但它确实包括了每个记录的计数。
    【解决方案2】:

    如果你愿意使用lodash(如果没有最好使用),你可以这样做,

    var array = [
        {
            _id: 1,
           browser: 'chrome',
           ipAddress: '222.111.111.0',
           uri: 'example1.com'
        },
        {
           _id: 2,
           browser: 'chrome',
           ipAddress: '222.111.111.0',
           uri: 'example1.com'
        },
        {
           _id: 3,
           browser: 'opera',
           ipAddress: '222.0.888.0',
           uri: 'example1.com'
        },
        {
           _id: 4,
           browser: 'chrome',
           ipAddress: '222.111.222.0',
           uri: 'sample1.com'
        },
        {
           _id: 5,
           browser: 'chrome',
           ipAddress: '222.111.222.0',
           uri: 'sample1.com'
        },
        {
           _id: 6,
           browser: 'chrome',
           ipAddress: '222.111.222.0',
           uri: 'sample1.com'
        },
        {
           _id: 7,
           browser: 'opera',
           ipAddress: '222.111.222.0',
           uri: 'sample1.com'
        }
    ]
    
    var res = _.reduce(array, function(acc, elem){
      delete elem._id;
      var obj = _.find(acc, elem)
      if(obj){
        obj.count++;
      }
      else{
        elem.count = 1;
        acc.push(elem);
      }
      return acc;
    }, [])
    
    console.log(res);
    <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>

    【讨论】:

    • 感谢 RaR。我不反对使用库,我知道 lodash、下划线等是不错的选择
    【解决方案3】:

    你可以试试这样的:

    var data=[{_id:1,browser:"chrome",ipAddress:"222.111.111.0",uri:"example1.com"},{_id:2,browser:"chrome",ipAddress:"222.111.111.0",uri:"example1.com"},{_id:3,browser:"opera",ipAddress:"222.0.888.0",uri:"example1.com"},{_id:4,browser:"chrome",ipAddress:"222.111.222.0",uri:"sample1.com"},{_id:5,browser:"chrome",ipAddress:"222.111.222.0",uri:"sample1.com"},{_id:6,browser:"chrome",ipAddress:"222.111.222.0",uri:"sample1.com"},{_id:7,browser:"opera",ipAddress:"222.111.222.0",uri:"sample1.com"}];
    
    function groupBy(array, keys) {
      var groups = array.reduce(function(p, c) {
        var hash = keys.map(function(k){ return c[k]; }).join("|")
        p[hash] = p[hash] || c;
        p[hash]["count"] = (p[hash]["count"] || 0) + 1
        delete p[hash]["_id"];
        return p;
      }, {});
      var result = Object.keys(groups).map(function(x){return groups[x] })
      console.log(result);
      return result
    }
    
    var keys = ["browser", "ipAddress", "uri"]
    groupBy(data, keys)

    【讨论】:

    • 谢谢拉杰什。当我运行它时,它正在分组,但我根本没有收到计数。
    • @Dave 我使用的是 ES6 代码。这可能会导致问题。已更新我的答案以使用 ES5 语法。希望对你有帮助
    • 与其说是 ES6/ES5,不如说是结果实际上并没有返回计数。我只接收组,还接收 _id 字段。
    【解决方案4】:

    您可以通过 vanilla JavaScript 使用单个 reduce 来做到这一点:

    let arr = [
        {
            _id: 1,
           browser: 'chrome',
           ipAddress: '222.111.111.0',
           uri: 'example1.com'
        },
        {
           _id: 2,
           browser: 'chrome',
           ipAddress: '222.111.111.0',
           uri: 'example1.com'
        },
        {
           _id: 3,
           browser: 'opera',
           ipAddress: '222.0.888.0',
           uri: 'example1.com'
        },
        {
           _id: 4,
           browser: 'chrome',
           ipAddress: '222.111.222.0',
           uri: 'sample1.com'
        },
        {
           _id: 5,
           browser: 'chrome',
           ipAddress: '222.111.222.0',
           uri: 'sample1.com'
        },
        {
           _id: 6,
           browser: 'chrome',
           ipAddress: '222.111.222.0',
           uri: 'sample1.com'
        },
        {
           _id: 7,
           browser: 'opera',
           ipAddress: '222.111.222.0',
           uri: 'sample1.com'
        }
    ]
    
    let result = arr.reduce((_, x) => {
      for(let i = 0; i < _.length; i++) {
        if(_[i].browser === x.browser && _[i].ipAddress === x.ipAddress && _[i].uri === x.uri) {
          _[i].count++
          return _
        }
      }
      let { _id, ...rest } = x
      return [ ..._, { ...rest, count: 1 } ]
    }, [])
    
    console.log(result)

    【讨论】:

    • 感谢 cchamberlain,我假设您为此使用下划线?
    • @Dave 不,这是原生 JavaScript。这里的下划线只是减速器累加器有些常用的变量。如果项目中有下划线或 lodash,则最好为累加器选择不同的变量名称。
    • @Dave 已通过 MDN 上的 reduce 文档的链接更新了答案。
    • 很遗憾,我无法让它运行。我的应用程序一直对“...”感到恐惧。我知道它是 ES6,而我的 NodeJS 是最新的,所以应该能够运行它,但一直给我一个关于 spread 属性的错误。
    【解决方案5】:

    您可以通过使用通用 reducer 生成器来实现这一点。此代码基于 my previous answer to another question 。你可以给它你想要分组的字段,它返回的函数可以作为减速器给出一个项目计数。

    let arr = Object.freeze([
        {
            _id: 1,
           browser: 'chrome',
           ipAddress: '222.111.111.0',
           uri: 'example1.com'
        },
        {
           _id: 2,
           browser: 'chrome',
           ipAddress: '222.111.111.0',
           uri: 'example1.com'
        },
        {
           _id: 3,
           browser: 'opera',
           ipAddress: '222.0.888.0',
           uri: 'example1.com'
        },
        {
           _id: 4,
           browser: 'chrome',
           ipAddress: '222.111.222.0',
           uri: 'sample1.com'
        },
        {
           _id: 5,
           browser: 'chrome',
           ipAddress: '222.111.222.0',
           uri: 'sample1.com'
        },
        {
           _id: 6,
           browser: 'chrome',
           ipAddress: '222.111.222.0',
           uri: 'sample1.com'
        },
        {
           _id: 7,
           browser: 'opera',
           ipAddress: '222.111.222.0',
           uri: 'sample1.com'
        }
    ]);
    
    const groupByReducerCount = (group) =>
      (result, row) => {
        const keygroup = group.map((v) => row[v]);
        const key = keygroup.join(':');
        if (result[key])
          result[key] ++;
        else
          result[key] = 1;
        return result;
      };
    
    
    const result = arr.reduce(groupByReducerCount(['uri','browser','ipAddress']),{});
    
    console.log(result)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-08-07
      • 2013-07-03
      • 2021-01-28
      • 1970-01-01
      • 1970-01-01
      • 2017-07-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多