RxJS操作符——数据流的过滤
上文中我们学习了数据流的各种创建方法,从这篇文章开始着重介绍对数据流的各种转化操作,首先从过滤系列操作说起。可能对于某个数据流,我们根据业务场景不同需要从中挑选出合适的数据,类似于数组的 filter,RxJS中也提供了一系列的方法供我们快捷过滤。
索引型过滤
有时候我们需要过滤出的数据和它的具体内容没有关系,只和它在数据流中的索引位置有关。这种过滤我统称为索引型过滤总结如下。
elementAt
如果只需要数据流中指定索引的单个元素,则可以用该方法。接受参数为从0开始的索引,也可以指定第二个参数作为缺省值。例子如下:
let source = Rx.Observable.of(1, 2, 3);
let target = source.elementAt(1);
// 1-2-3-|
// elementAt
// 2-|
first/last
这两个方法可以传入一个断言函数过滤出第一个/最后一个满足条件的元素。但大多数时候不传入断言函数,则直接过滤出第一个/最后一个元素。如果传入断言函数后没找到有效值时,也可以传入一个参数作为默认值。
let source = Rx.Observable.of(1, 2, 3);
let target = source.first();
// 1-2-3-|
// first
// 1-|
take/takeLast
该方法过滤出指定数量的最初/最后元素后结束数据流。如果参数值大于发出值则发出所有值。
let source = Rx.Observable.of(1, 2, 3);
let target = source.take(2);
// 1-2-3-|
// take
// 1-2-|
需要注意的是takeLast必须等待数据流完成通知后才能过滤,因为不这样的话无法知道是否还有更多值发出。出于这个原因,所有值将同步发出并完成。
let source = Rx.Observable.of(1, 2, 3);
let target = source.takeLast(1);
// 1-2-3-|
// takeLast
// ----3-|
ignoreElements
该方法可以忽略所有元素仅过滤出其结束通知。
let source = Rx.Observable.of(1, 2, 3);
let target = source.ingoreElements();
// 1-2-3-|
// ingoreElements
// ------|
条件型过滤
这类过滤方式通常由一个断言函数决定,当不满足条件时则过滤掉。当然也可以由其他因素来决定,例如时间,重复性等。
断言函数过滤
filter
此方法和数组的filter非常相似,接受一个断言函数,参数为数据流的值和索引。也可以传入第二个参数指定断言函数的上下文。例子如下:
let source = Rx.Observable.of(1, 2, 3);
let target = source.filter(d => d < 3);
// 1-2-3-|
// filter
// 1-2---|
find/findIndex
该方法与数组的find方法类似,通过一个断言函数过滤出数据流中第一个满足条件的元素,该参数是必须的。也可以通过第二个参数决定断言函数的上下文。例子如下:
let source = Rx.Observable.of(1, 2, 3, 3);
let target = source.find(x => x === 3);
// 1-2-3-3-|
// find
// ----3-|
findIndex用法和find也完全相同,区别在于查询元素为索引。
takeWhile
该方法会从数据流中最初的元素开始遍历并使用传入的断言函数判定,过滤出满足条件的元素,直到遇到不满足的元素就立即结束数据流。
let source = Rx.Observable.interval(1000);
let target = source.takeWhile(item => item < 5);
// --0--1--2--3--4--…………
// takeWhile
// --0--1--2--3--4-|
重复过滤
distinct/distinctUntilChanged/distinctUntilKeyChanged
distinct相关方法用来过滤出相同的元素,不传任何参数即可使用。如果数据流元素不是基本类型,我们可以传入一个键选择函数决定需要过滤的相同键。例子如下:
let data = [
{ id: 11 },
{ id: 22 },
{ id: 11 }
];
let source = Rx.Observable.from(data);
let ksf = d => d.id;
let target = source.distinct(ksf);
// {id: 11}-{id: 22}-{id: 11}-|
// distinct
// {id: 11}-{id: 22}---------|
也可以传入第二个参数——比较函数来更改比较规则。
let source = Rx.Observable.from('aABb');
let kcf = (d1, d2) => d1.toLowerCase() === d2.toLowerCase();
let target = source.distinct(null, kcf);
//a-A-B-b-|
// distinct
//a---B----|
distinctUntilChanged方法的使用和distinct完全相同,唯一区别在于仅过滤连续出现的相同元素。
distinctUntilKeyChanged则是键比较的简化版,代替键选择函数,只需传入需要比较的键即可。
时间过滤
debounceTime
该方法和去抖函数类似,接受一个静默时间参数,会在将要发送某值时先将其缓存住,静默以毫秒为单位的该值,这段时间内发送的值会被重新缓存并重新静默,直到在静默时间内不再发送新值时则发出缓存值。
let clicks = Rx.Observable.fromEvent(document, 'click');
let result = clicks.debounceTime(1000);
// -c-c-c---c-c-c---c-……
// debounceTime(1000)
// --------c--- ---c-----c-……
throttleTime
该方法和节流函数类似,同样接受一个静默时间参数,发送某值后会静默以毫秒为单位的该值,这段时间内发送的值会被全部过滤掉。
let clicks = Rx.Observable.fromEvent(document, 'click');
let result = clicks.throttleTime(1000);
// --c-c-c-c-c-c-c-c-c-c……
// throttleTime(1000)
// --c-----c-----c-----c……
sampleTime
该方法接受一个时间参数,会以该时间参数为周期定期去源数据流取样当前周期内流出的最新值,如果取到新值则发出,否则不发出。
let clicks = Rx.Observable.fromEvent(document, 'click');
let result = clicks.sampleTime(1000);
// -c------c-c----……
// sampleTime(1000)
// ---c--------c--……
auditTime
该方法和节流函数类似,区别在于节流函数立即发出数据后静默时间参数,而该方法遇到原数据流流出值后先静默时间参数后再发出原值数据流的最新流出值。
let clicks= Rx.Observable.fromEvent(document, 'click');
let result = clicks.auditTime(1000);
// --c-c-c-c-c-c-c-c-c-……
// auditTime(1000)
// -----c----c----c----c……
debounce/throttle/sample/audit
原理都和以上相同,区别在于时间参数不由自己手动传入毫秒决定,而是根据传入的数据流发出值的时间决定。
skip系列方法
该系列方法和take互补,由选择改成不选择,使用方法完全相同。就不单独列出。
-- EOF --