【问题标题】:Count how many and which index of a array计算数组的数量和索引
【发布时间】:2014-09-16 01:10:12
【问题描述】:

我有一个对象数组:

 result = [
 { _id: 53d0dfe3c42047c81386df9d, video_id: '1' },
 { _id: 53d0dfe3c42047c81386df9e, video_id: '1' },
 { _id: 53d0dfe3c42047c81386df9f, video_id: '1' },
 { _id: 53d0dfe3c42047c81386dfa0, video_id: '2' },
 { _id: 53d0dfe3c42047c81386dfa1, video_id: '2' },
 { _id: 53d0dfe3c42047c81386dfa2, video_id: '1' },
 { _id: 53d0dfe3c42047c81386dfa3, video_id: '2' },
 { _id: 53d0dfe3c42047c81386dfa4, video_id: '1' } 
 ]

我需要创建另一个数组,以video_id为索引,包含这个video_id在第一个数组中出现的次数:

list = [
{'1' : 5},
{'2' : 4}
]

目前,我使用以下代码:

while (i < result.length)
{
    if(list[result[i].video_id] === undefined) {
        list[result[i].video_id] = 0;
    }
    list[result[i].video_id] = list[result[i].video_id] + 1;
    i = i + 1;
}

它有效,但我想知道是否有更快更清洁的方法来做到这一点? (实际结果数组有超过 10k 个元素,我怀疑 >10k 条件语句是最优的......)。

我正在使用 node.js,结果来自 mongoose (mongoDB) 查询,我没有看到任何方法可以通过 mongoose 本身完成此操作:

var now = new Date();
//M_logs is a mongoose model
query = M_logs.where('time').gt(new Date(now.getFullYear(), 0, 1).getTime() / 1000).lt(now.getTime() / 1000).select('video_id');

(PS:我想知道这不是代码审查问题,如果我跑题了,请告诉我,以便我可以迁移问题)。

编辑:

回答胡安·卡洛斯·法拉:

S_logs =  new mongoose.Schema({

    user_ip : String,
    user_id : String,
    user_agent : String,
    canal_id :  String,
    theme_id :  String,
    video_id :  String,
    osef : String,
    time : Number,
    action: String,
    is_newuser : String,
    operator : String,
    template : String,
    catalogue : String,
    referer : String,
    from : String,
    osef1 : String

});

M_logs = mongoose.model('logs', S_logs);

【问题讨论】:

  • M_logs中的文档是什么样的?
  • @JuanCarlosFarah:添加
  • 太棒了。刚刚添加了一个答案。

标签: arrays node.js mongodb mongoose


【解决方案1】:

您可以使用聚合框架来做到这一点。这个想法是做如下事情:

  1. 匹配您要查找的文档。根据您当前的查询,我知道这将是 time 介于 new Date(now.getFullYear(), 0, 1).getTime() / 1000now.getTime() / 1000 之间的文档。
  2. 将匹配的文档按video_id 分组并跟踪其计数。
  3. 可选择按_id 排序,这相当于原来的video_id

以下是mongo shell 语法:

var now = new Date();

db.M_logs.aggregate([
    {
        "$match" : {
            "time" : { 
                "$gt" : new Date(now.getFullYear(), 0, 1).getTime() / 1000,
                "$lt" : now.getTime() / 1000
            }
        }    
    },
    { 
        "$group" : {
            "_id" : "$video_id",
            "count" : { "$sum" : 1 }
        }
    },
    { 
        "$sort" : { "_id" : 1 }
    }
]);

如果这对您有用,您可以轻松地用 Mongoose 或 Node.js 驱动程序语法实现它。请注意,聚合框架返回一个游标,您可以遍历该游标来填充您的数组。

编辑:

使用 Node.js 驱动程序,您可以在回调函数中访问聚合查询的结果。如下:

...
, function(err, result) {
    console.dir(result);
    db.close();                          
}

请注意,聚合查询的 Mongoose 语法略有不同。

例子:

Model.aggregate([ <QUERY> ]).exec( <CALLBACK> );

有关更多信息,请参阅文档here

【讨论】:

  • 能补充一下光标的使用方法吗?到目前为止,这正是我所需要的:)(也感谢对我的问题的语法编辑)
  • @DrakaSAN。不是问题。是的,我会将其添加到我的答案中。
【解决方案2】:

我建议您使用aggregation framework 来计算文档数量。它比迭代所有文档并计算它们要快得多。

使用猫鼬你可以这样做:

var now = new Date();
var startTime = new Date(now.getFullYear(), 0, 1).getTime() / 1000):
var endTime = now.getTime() / 1000;

M_logs.aggregate([
    // filter the documents you're looking for
    {"$match" : { "time" : {"$gt": startTime, "$lt": endTime}}},
    // group by to get the count for each video_id
    {"$group" : {"_id" : "$video_id", "count" : {"$sum" : 1}}},
    // make the output more explanatory; this part is optional
    {"$project" : { "video_id" : "$_id", "count" : "$count", _id : 0}}
]).exec(function(err, docs){
    if (err) console.err(err);
    console.log(docs);
});

docs 的输出将是:

[ { count: 4, video_id: '2' }, { count: 5, video_id: '1' } ]

【讨论】:

    【解决方案3】:

    使用

    var list = {};
    result.forEach(function (el) {
        list[el.video_id] = (list[el.video_id] || 0) + 1;
    });
    

    结果列表如下所示:

    var list = {
        '1': 5,
        '2': 4
    };
    

    【讨论】:

    • 确实有效,我稍等一下,看看是否出现其他解决方案:)
    猜你喜欢
    • 2013-08-14
    • 1970-01-01
    • 1970-01-01
    • 2020-09-07
    • 1970-01-01
    • 2010-11-29
    • 2015-09-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多