前言

真是天一热什么事都不想干,这个月只产出了一篇文章,赶紧写一篇压压惊!

前文(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 的设计原理的更多相关文章

  1. Unity iOS打开AppStore评星页面,浅谈Application.OpenURL()方法。

    http://fairwoodgame.com/blog/?p=38 Unity iOS打开AppStore评星页面,浅谈Application.OpenURL()方法. Posted in  Uni ...

  2. Java网络编程和NIO详解7:浅谈 Linux 中NIO Selector 的实现原理

    Java网络编程和NIO详解7:浅谈 Linux 中NIO Selector 的实现原理 转自:https://www.jianshu.com/p/2b71ea919d49 本系列文章首发于我的个人博 ...

  3. 浅谈iOS中MVVM的架构设计与团队协作

    说到架构设计和团队协作,这个对App的开发还是比较重要的.即使作为一个专业的搬砖者,前提是你这砖搬完放在哪?不只是Code有框架,其他的东西都是有框架的,比如桥梁等等神马的~在这儿就不往外扯了.一个好 ...

  4. IOS中 浅谈iOS中MVVM的架构设计与团队协作

    今天写这篇文章是想达到抛砖引玉的作用,想与大家交流一下思想,相互学习,博文中有不足之处还望大家批评指正.本篇文章的内容沿袭以往博客的风格,也是以干货为主,偶尔扯扯咸蛋(哈哈~不好好工作又开始发表博客啦 ...

  5. 浅谈iOS中MVVM的架构设计与团队协作【转载】

    今天写这篇文章是想达到抛砖引玉的作用,想与大家交流一下思想,相互学习,博文中有不足之处还望大家批评指正.本篇文章的内容沿袭以往博客的风格,也是以干货为主,偶尔扯扯咸蛋(哈哈~不好好工作又开始发表博客啦 ...

  6. 数据层的多租户浅谈(SAAS多租户数据库设计)

    在上一篇“浅析多租户在 Java 平台和某些 PaaS 上的实现”中我们谈到了应用层面的多租户架构,涉及到 PaaS.JVM.OS 等,与之相应的是数据层也有多租户的支持. 数据层的多租户综述 多租户 ...

  7. [转载]数据层的多租户浅谈(SAAS多租户数据库设计)

    原文:http://www.ibm.com/developerworks/cn/java/j-lo-dataMultitenant/index.html 在上一篇“浅析多租户在 Java 平台和某些 ...

  8. 浅谈MatrixOne如何用Go语言设计与实现高性能哈希表

    目录 MatrixOne数据库是什么? 哈希表数据结构基础 哈希表基本设计与对性能的影响 碰撞处理 链地址法 开放寻址法 Max load factor Growth factor 空闲桶探测方法 一 ...

  9. 浅谈我对DDD领域驱动设计的理解

    从遇到问题开始 当人们要做一个软件系统时,一般总是因为遇到了什么问题,然后希望通过一个软件系统来解决. 比如,我是一家企业,然后我觉得我现在线下销售自己的产品还不够,我希望能够在线上也能销售自己的产品 ...

随机推荐

  1. css3相册图片3D旋转展示特效

    查看效果:http://hovertree.com/texiao/css/14/ 本效果用css3的animation实现动画 定义和用法animation 属性是一个简写属性,用于设置六个动画属性: ...

  2. JavaScript数组的reduce方法详解

    数组经常用到的方法有push.join.indexOf.slice等等,但是有一个经常被我们忽略的方法:reduce,这个方法简直强大的不要不要的. 我们先来看看这个方法的官方概述:reduce()  ...

  3. SharePoint 2013 Excel Services ECMAScript 示例之明日限行

    前言:最近遇到一个“明日限行”的功能,北京的交通啊,这个不在今天讨论范围内,暂不吐槽,想想代码开发,还要写WebPart部署,很麻烦,而且部署服务器,需要领导审批,想绕过这个麻烦事儿,就想到客户端了, ...

  4. 【原】Github系列之二:开源 一行代码实现多形式多动画的推送小红点WZLBadge(iOS)

    更新日志 V1.2 2015.09.25 1.UITabBarItem badge is supproted; 2.Enable change badge properties when badge ...

  5. php设计模式 装饰器模式

    装饰器模式,可以动态地添加修改类的功能. 一个类提供了一项功能,如果要修改并添加额外的功能,传统的编程模式需要写一个子类继承它,并重新实现类的方法.使用装饰器模式,仅需要在运行时添加一个装饰器对象即可 ...

  6. Redis 支持的5种数据结构

    redis的崛起绝非偶然,它确实有自己的新东西在里面,它不像Memcached,只能将数据存储在内存中,它提供了持久化机制和数据同步,避免了宕机后的雪崩的问题,即服务器出现问题后,内存中保留的原始数据 ...

  7. 一道常被人轻视的前端JS面试题

    前言 年前刚刚离职了,分享下我曾经出过的一道面试题,此题是我出的一套前端面试题中的最后一题,用来考核面试者的JavaScript的综合能力,很可惜到目前为止的将近两年中,几乎没有人能够完全答对,并非多 ...

  8. apache+mysql+php的环境配置

    一 配置前的准备 1 先设置环境变量(win7的) win10 二 配置apache 我用EditPlus打开httpd.conf LoadModule php5_module  "c:/w ...

  9. 正则表达式校验URL

    正则表达式: var match = /^((ht|f)tps?):\/\/([\w-]+(\.[\w-]+)*\/?)+(\?([\w\-\.,@?^=%&:\/~\+#]*)+)?$/; ...

  10. 从零自学Hadoop(07):Eclipse插件

    阅读目录 序 Eclipse Eclipse插件 新建插件项目 系列索引 本文版权归mephisto和博客园共有,欢迎转载,但须保留此段声明,并给出原文链接,谢谢合作. 文章是哥(mephisto)写 ...