浅谈 underscore 内部方法 group 的设计原理
前言
真是天一热什么事都不想干,这个月只产出了一篇文章,赶紧写一篇压压惊!
前文(https://github.com/hanzichi/underscore-analysis/issues/15)说到楼主开始解读 underscore.js 中的 Collection Functions 部分,看了一遍这部分的源码,很多方法都是一看便懂,不需要楼主过分解读。本文来聊聊内部方法 group 的设计,这会是 Collection Functions 部分的第二篇文章,也是最后一篇文章,关于这部分其他方法的实现,可以看我的 全文源码注释。
_.groupBy & _.indexBy & _.countBy
group 是 underscore.js 中的一个内部方法,顾名思义,为了分组而用。有三个 API 用到了这个方法,分别是 _.groupBy,_.indexBy,_.countBy。
来看看这三个 API 的作用。
_.groupBy,可以对一个数组的元素进行分组,如何分组?可以将元素传入一个迭代函数,根据迭代后的值进行分组,也可以传入一个字符串表示元素属性,根据该属性值进行分组。
_.groupBy([1.3, 2.1, 2.4], function(num){ return Math.floor(num); });
=> {1: [1.3], 2: [2.1, 2.4]}
_.groupBy(['one', 'two', 'three'], 'length');
=> {3: ["one", "two"], 5: ["three"]}
可以看到,返回的结果是一个对象,key 为经过迭代函数迭代后的值,或者属性值,value 为一个数组,保存迭代结果或者属性值一样的元素。
再来看 _.indexBy。
var stooges = [{name: 'moe', age: 40}, {name: 'larry', age: 50}, {name: 'curly', age: 60}];
_.indexBy(stooges, 'age');
=> {
"40": {name: 'moe', age: 40},
"50": {name: 'larry', age: 50},
"60": {name: 'curly', age: 60}
}
跟 _.groupBy 差不多,不同的是,_.indexBy 的结果,每个 key 值对应的是一个元素(传入 _.indexBy 方法的数组中的元素),而不是一个数组(Just like groupBy, but for when you know your keys are unique.)。如果原来数组中,有两个元素,经过迭代后(或者元素属性值)相同,那么在结果对象中,后者会覆盖前者。(所以最好确认数组中的元素经过迭代后的值没有相同的,或者属性值没有相同的)
var tmp = _.indexBy(['one', 'two', 'three'], 'length');
=> Object {3: "two", 5: "three"}
最后来看 _.countBy。还是返回一个结果对象,它的 key 值意思还是和 _.groupBy 以及 _.indexBy 相同,而 value 值为迭代结果(或者属性值)是该 key 值的元素的个数。
_.countBy([1, 2, 3, 4, 5], function(num) {
return num % 2 == 0 ? 'even': 'odd';
});
=> {odd: 3, even: 2}
group
这三个 API 功能相近,难道要写三个独立的方法?如果独立写,大概会是这样。
_.groupBy = function(obj, iteratee, context) {
// 返回结果是一个对象
var result = {};
// 根据 iteratee 值确定迭代函数
iteratee = cb(iteratee, context);
// 遍历元素
_.each(obj, function(value, index) {
// 经过迭代,获取结果值,存为 key
var key = iteratee(value, index, obj);
// TODO
// ...
});
// 返回结果对象
return result;
};
_.indexBy = function(obj, iteratee, context) {
// 返回结果是一个对象
var result = {};
// 根据 iteratee 值确定迭代函数
iteratee = cb(iteratee, context);
// 遍历元素
_.each(obj, function(value, index) {
// 经过迭代,获取结果值,存为 key
var key = iteratee(value, index, obj);
// TODO
// ...
});
// 返回结果对象
return result;
};
_.countBy = function(obj, iteratee, context) {
// 返回结果是一个对象
var result = {};
// 根据 iteratee 值确定迭代函数
iteratee = cb(iteratee, context);
// 遍历元素
_.each(obj, function(value, index) {
// 经过迭代,获取结果值,存为 key
var key = iteratee(value, index, obj);
// TODO
// ...
});
// 返回结果对象
return result;
};
大堆功能相似的代码,简直不能忍!每当这个时候,就要想到闭包,函数嵌套函数!
首先定义个函数 group,返回一个函数,为以上三个方法能调用的函数。听起来有点拗口,其实就是用 group 做个中间层,上代码体会下。
var group = function() {
return function(obj, iteratee, context) {
// 返回结果是一个对象
var result = {};
// 根据 iteratee 值确定迭代函数
iteratee = cb(iteratee, context);
// 遍历元素
_.each(obj, function(value, index) {
// 经过迭代,获取结果值,存为 key
var key = iteratee(value, index, obj);
// TODO
// ...
});
// 返回结果对象
return result;
};
};
_.groupBy = group();
_.indexBy = group();
_.countBy = group();
其实三个方法主要的操作,就是对上面的 group 中 result 对象的操作,我们可以传入一个方法,利用该方法对 result 对象进行操作。
以 _.groupBy 方法为例:
var group = function(behavior) {
return function(obj, iteratee, context) {
// 返回结果是一个对象
var result = {};
// 根据 iteratee 值确定迭代函数
iteratee = cb(iteratee, context);
// 遍历元素
_.each(obj, function(value, index) {
// 经过迭代,获取结果值,存为 key
var key = iteratee(value, index, obj);
// operate
behavior(result, value, key);
});
// 返回结果对象
return result;
};
};
var behavior = function(result, value, key) {
if (_.has(result, key))
result[key].push(value);
else result[key] = [value];
}
_.groupBy = group(behavior);
将对象当做参数传入函数,能在函数内改变对象值,正是利用了这个特点。而 _.indexBy 和 _.countBy,无非是改一下 behavior 函数的事了。
酱油了一篇解读,下文开始讲扩展函数了,其实最开始有解读 underscore 的欲望,正是因为函数部分的节流和去抖。
浅谈 underscore 内部方法 group 的设计原理的更多相关文章
- Unity iOS打开AppStore评星页面,浅谈Application.OpenURL()方法。
http://fairwoodgame.com/blog/?p=38 Unity iOS打开AppStore评星页面,浅谈Application.OpenURL()方法. Posted in Uni ...
- Java网络编程和NIO详解7:浅谈 Linux 中NIO Selector 的实现原理
Java网络编程和NIO详解7:浅谈 Linux 中NIO Selector 的实现原理 转自:https://www.jianshu.com/p/2b71ea919d49 本系列文章首发于我的个人博 ...
- 浅谈iOS中MVVM的架构设计与团队协作
说到架构设计和团队协作,这个对App的开发还是比较重要的.即使作为一个专业的搬砖者,前提是你这砖搬完放在哪?不只是Code有框架,其他的东西都是有框架的,比如桥梁等等神马的~在这儿就不往外扯了.一个好 ...
- IOS中 浅谈iOS中MVVM的架构设计与团队协作
今天写这篇文章是想达到抛砖引玉的作用,想与大家交流一下思想,相互学习,博文中有不足之处还望大家批评指正.本篇文章的内容沿袭以往博客的风格,也是以干货为主,偶尔扯扯咸蛋(哈哈~不好好工作又开始发表博客啦 ...
- 浅谈iOS中MVVM的架构设计与团队协作【转载】
今天写这篇文章是想达到抛砖引玉的作用,想与大家交流一下思想,相互学习,博文中有不足之处还望大家批评指正.本篇文章的内容沿袭以往博客的风格,也是以干货为主,偶尔扯扯咸蛋(哈哈~不好好工作又开始发表博客啦 ...
- 数据层的多租户浅谈(SAAS多租户数据库设计)
在上一篇“浅析多租户在 Java 平台和某些 PaaS 上的实现”中我们谈到了应用层面的多租户架构,涉及到 PaaS.JVM.OS 等,与之相应的是数据层也有多租户的支持. 数据层的多租户综述 多租户 ...
- [转载]数据层的多租户浅谈(SAAS多租户数据库设计)
原文:http://www.ibm.com/developerworks/cn/java/j-lo-dataMultitenant/index.html 在上一篇“浅析多租户在 Java 平台和某些 ...
- 浅谈MatrixOne如何用Go语言设计与实现高性能哈希表
目录 MatrixOne数据库是什么? 哈希表数据结构基础 哈希表基本设计与对性能的影响 碰撞处理 链地址法 开放寻址法 Max load factor Growth factor 空闲桶探测方法 一 ...
- 浅谈我对DDD领域驱动设计的理解
从遇到问题开始 当人们要做一个软件系统时,一般总是因为遇到了什么问题,然后希望通过一个软件系统来解决. 比如,我是一家企业,然后我觉得我现在线下销售自己的产品还不够,我希望能够在线上也能销售自己的产品 ...
随机推荐
- AngularJS 参考手册
AngularJS 参考手册 AngularJS 指令 本教程用到的 AngularJS 指令 : 指令 描述 ng-app 定义应用程序的根元素. ng-bind 绑定 HTML 元素到应用程序数据 ...
- SharePoint 2013 图文开发系列之计时器任务
SharePoint的计时器任务,又称TimerJob,由服务里的Timer服务执行,在管理中心管理,是一个类似于Windows任务计划的功能,方便定时执行一些需要的功能,以免影响服务器性能. 在Sh ...
- IOS开发基础知识--碎片48
1:Assertion failure in dequeueReusableCellWithIdentifier:forIndexPath: static NSString *CellIdentif ...
- mysql远程登录
mysql -h -P -u -p-h:需要登录的mysql服务器的ip-P(大写):mysql开放的端口,如果是3306端口可省略此选项-u:数据库用户名-p:数据库密码
- IOS RunLoop浅析 二
上一篇我们说了runloop 的几种模式,那么我们在模式中又要做些什么呢??? 模式中有三个模块: 事件源(输入源) Source Source: 按照官方文档分类 Port-Based Custom ...
- 2014年年度工作总结--IT狂人实录
2014年也是我人生最重要的一年,她见证了我的成长与蜕变,让我从一个迷茫的旅者踏上一条柳暗花明的路. 春宇之行 从春宇短暂的9个月,却经历常人难以想想的风风雨雨,首先要感谢春宇公司给我带来了安逸宽松的 ...
- Windows平台下利用APM来做负载均衡方案 - 负载均衡(下)
概述 我们在上一篇Windows平台分布式架构实践 - 负载均衡中讨论了Windows平台下通过NLB(Network Load Balancer) 来实现网站的负载均衡,并且通过压力测试演示了它的效 ...
- Windows系统下Oracle每天自动备份
linux和unix下面使用shell可以很方便实现,如果windows环境下可以结合计划任务实现 创建备份目录d:\backup, 创建批处理命令Bak.bat,编写备份脚本 exp user/pa ...
- Java 流(Stream)、文件(File)和IO
Java.io包几乎包含了所有操作输入.输出需要的类.所有这些流类代表了输入源和输出目标. Java.io包中的流支持很多种格式,比如:基本类型.对象.本地化字符集等等. 一个流可以理解为一个数据的序 ...
- PHP笔记(PHP中级篇)
初级了解PHP的语法,中级就要学习PHP操作DateBase以及各种复杂的实现了! 文件系统处理 作用: 项目需要 长时间保存数据 服务器中文件操作 特点 都是使用系统函数完成的 基于Linux/Un ...