JS对大量数据进行多重过滤的方法


Posted in Javascript onNovember 04, 2016

前言

主要的需求是前端通过 Ajax 从后端取得了大量的数据,需要根据一些条件过滤,首先过滤的方法是这样的:

class Filter {
 filterA(s) {
 let data = this.filterData || this.data;
 this.filterData = data.filter(m => m.a === s);
 }
 
 filterB(s) {
 let data = this.filterData || this.data;
 this.filterData = data.filter(m => m.b === s);
 }
}

现在迷糊了,觉得这样处理数据不对,但是又不知道该怎么处理。

发现问题

问题就在过滤上,这样固然可以实现多重过滤(先调用 filterA() 再调用 filterB() 就可以实现),但是这个过滤是不可逆的。

假如过滤过程是这样:

f.filterA("a1");
f.filterB("b1");
f.filterA("a2");

本来是希望按 "a1" 和 "b1" 过滤了数据之后,再修改第一个条件为 "a2",但结果却成了空集。

解决问题

发现了问题,就针对性的解决。这个问题既然是因为过滤过程不可逆造成的,那每次都直接从 this.data 开始过滤,而不是从 this.filterData 开始过滤,就能解决问题。如果要这样做,就需要将选择的过滤条件先记录下来。

记录过滤条件

用一个列表记录过滤条件当然是可行的,但是注意对同一个条件的两次过滤是互斥的,只能保留最后一个,所以应该用 HashMap 更为合适。

class Filter {
 constructor() {
 this.filters = {};
 }

 set(key, filter) {
 this.filters[key] = filter;
 }

 getFilters() {
 return Object.keys(this.filters).map(key => this.filters[key]);
 }
}

这种情况下,像上面的过程表示为

f.set("A", m => m.a === "a1");
f.set("B", m => m.b === "b1");
f.set("A", m => m.a === "a1");

let filters = f.getFilters(); // length === 2;

上面第 3 句设置的 filter 覆盖了第 1 句设置的那个。现在再用最后取得的 filters 依次来过滤原数据 this.data,就能得到正确的结果。

有人会觉得 getFilters() 返回的列表不是按 set 的顺序的——的确,这是 HashMap 的特点,无序。不过对于简单条件的判断,不管谁先谁后,结果是一样的。但是对于一些复合条件判断,就可能会有影响。

确实需要的话,可以通过 array 代替 map 来解决一下顺序的问题,但这样查找效率会降低(线性查找)。如果还想解决查找效率的问题,可以用 array + map 来处理。这里就不多说了。

过滤

实际上在使用的时候,每次都 getFilter() 再用一个循环来处理确实比较慢。既然 data 都封装成 Filter 中,可以考虑直接给一个 filter() 方法来送货过滤接口。

class Filter {
 filter() {
 let data = this.data;
 for (let f of this.getFilters()) {
  data = data.filter(f);
 }
 return data;
 }
}

不过这样我觉得效率不太好,尤其是对大量数据的时候。不妨利用一下 lodash 的延迟处理过程。

利用 lodash 的延迟处理

filter() {
 let chain = _(this.data);
 for (let f of this.getFilters()) {
 chain = chain.filter(f);
 }
 return chain.value();
}

lodash 在数据大于 200 的时候会启用延迟处理过程,也就是说,它会处理成一个循环中依次调用每一个 filter,而不是对每一个 filter 进行一次循环。

延迟处理和非延迟处理通过下图可以看出来区别。非延迟处理总共会进行 n(这里 n = 3) 次大循环,产生 n - 1 个中间结果。而延迟处理只会进行一次大循环,没有中间结果产生。

JS对大量数据进行多重过滤的方法

不过说实在的,我不太喜欢为了一点小事多加载一个库,所以干脆自己做个简单的实现

自己实现延迟处理

filter() {
 const filters = this.getFilters();
 return data.filter(m => {
 for (let f of filters) {
  // 如果某个 filter 已经把它过滤掉了,也不用再用后面的 filter 来判断了
  if (!f(m)) {
  return false;
  }
 }
 return true;
 });
}

里面的 for 循环还可以用 Array.prototype.every 来简化:

filter() {
 const filters = this.getFilters();
 return data.filter(m => {
 return filters.every(f => f(m));
 });
}

数据过滤其实并不是多复杂的事情,只要把思路理清楚,搞明白什么数据是需要保留的,什么数据是临时(中间过程)的,什么数据是最终结果……利用 Array.prototype 中的相关方法,或者诸如 lodash 之类的工具,很容易就处理出来了。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能有所帮助,如果有疑问大家可以留言交流。

Javascript 相关文章推荐
最近项目写了一些js,水平有待提高
Jan 31 Javascript
浅谈EasyUI中Treegrid节点的删除
Mar 01 Javascript
JS实现的4种数字千位符格式化方法分享
Mar 02 Javascript
javascript为按钮注册回车事件(设置默认按钮)的方法
May 09 Javascript
实例讲解JS中setTimeout()的用法
Jan 28 Javascript
JS组件Bootstrap实现图片轮播效果
May 16 Javascript
jquery实现多次上传同一张图片
Jan 09 Javascript
JavaScript的Object.defineProperty详解
Jul 09 Javascript
JavaScript遍历数组的三种方法map、forEach与filter实例详解
Feb 27 Javascript
微信小程序-form表单提交代码实例
Apr 29 Javascript
浅谈vue中组件绑定事件时是否加.native
Nov 09 Javascript
vue 项目打包时样式及背景图片路径找不到的解决方式
Nov 12 Javascript
AngularJS模板加载用法详解
Nov 04 #Javascript
jQuery 遍历map()方法详解
Nov 04 #Javascript
Sortable.js拖拽排序使用方法解析
Nov 04 #Javascript
jQuery图片加载显示loading效果
Nov 04 #Javascript
JavaScript 深层克隆对象详解及实例
Nov 03 #Javascript
jQuery.ajax实现根据不同的Content-Type做出不同的响应
Nov 03 #Javascript
简单学习vue指令directive
Nov 03 #Javascript
You might like
选择PHP作为网站开发语言的原因分享
2012/01/03 PHP
解析PHP汉字转换拼音的类
2013/06/18 PHP
浅析php中常量,变量的作用域和生存周期
2013/08/10 PHP
php调用KyotoTycoon简单实例
2015/04/02 PHP
php专用数组排序类ArraySortUtil用法实例
2015/04/03 PHP
php生成4位数字验证码的实现代码
2015/11/23 PHP
php使用ffmpeg获取视频信息并截图的实现方法
2016/05/03 PHP
php curl 模拟登录并获取数据实例详解
2016/12/22 PHP
扩展jquery实现客户端表格的分页、排序功能代码
2011/03/16 Javascript
jquery实现带复选框的表格行选中删除时高亮显示
2013/08/01 Javascript
60个很实用的jQuery代码开发技巧收集
2014/12/15 Javascript
一道常被人轻视的web前端常见面试题(JS)
2016/02/15 Javascript
Bootstrap 最常用的JS插件系列总结(图片轮播、标签切换等)
2016/07/14 Javascript
jQuery html表格排序插件tablesorter使用方法详解
2017/02/10 Javascript
JS库中的Particles.js在vue上的运用案例分析
2017/09/13 Javascript
JS设计模式之数据访问对象模式的实例讲解
2017/09/30 Javascript
angular6.x中ngTemplateOutlet指令的使用示例
2018/08/09 Javascript
javascript异步编程的六种方式总结
2019/05/17 Javascript
Nuxt的动态路由和参数校验操作
2020/11/09 Javascript
Nodejs 微信小程序消息推送的实现
2021/01/20 NodeJs
[03:22]DAC最前线(第二期)—DOTA2亚洲邀请赛主赛场周边及线路探访
2015/01/24 DOTA
[42:32]VP vs RNG 2019国际邀请赛淘汰赛 败者组 BO3 第一场 8.21.mp4
2020/07/19 DOTA
Python3批量移动指定文件到指定文件夹方法示例
2019/09/02 Python
python编写计算器功能
2019/10/25 Python
Python守护进程实现过程详解
2020/02/10 Python
Python操作Excel工作簿的示例代码(\*.xlsx)
2020/03/23 Python
完美解决ARIMA模型中plot_acf画不出图的问题
2020/06/04 Python
python通过cython加密代码
2020/12/11 Python
涂鸦板简单实现 Html5编写属于自己的画画板
2016/07/05 HTML / CSS
渡河少年教学反思
2014/02/12 职场文书
学雷锋标兵事迹材料
2014/08/18 职场文书
2015新年寄语(一句话)
2014/12/08 职场文书
鉴史问廉观后感
2015/06/10 职场文书
高中升旗仪式主持词
2015/07/03 职场文书
2016银行招聘自荐信
2016/01/28 职场文书
24年收藏2000多部退役军用电台
2022/02/18 无线电