为什么我们需要使用 flatMap?
Why do we need to use flatMap?
我开始使用 RxJS,我不明白为什么在这个例子中我们需要使用像 flatMap
或 concatAll
这样的函数;这里的数组数组在哪里?
var requestStream = Rx.Observable.just('https://api.github.com/users');
var responseMetastream = requestStream
.flatMap(function(requestUrl) {
return Rx.Observable.fromPromise(jQuery.getJSON(requestUrl));
});
responseMetastream.subscribe(url => {console.log(url)})
如果有人能直观地解释发生了什么,那将非常有帮助。
这不是数组的数组。它是可观察对象的可观察对象。
以下returns一个可观察的字符串流。
requestStream
.map(function(requestUrl) {
return requestUrl;
});
While this returns an observable stream of observable stream of json
requestStream
.map(function(requestUrl) {
return Rx.Observable.fromPromise(jQuery.getJSON(requestUrl));
});
flatMap
自动为我们展平 observable 以便我们可以直接观察 json 流
当我开始看Rxjs
时,我也偶然发现了那块石头。对我有帮助的是:
- 来自 reactivex.io 的文档。例如,对于
flatMap
:http://reactivex.io/documentation/operators/flatmap.html
- 来自 rxmarbles 的文档:http://rxmarbles.com/。您不会在那里找到
flatMap
,您必须查看 mergeMap
(另一个名称)。
- 您遗漏的 Rx 简介:https://gist.github.com/staltz/868e7e9bc2a7b8c1f754。它解决了一个非常相似的例子。特别是它解决了这样一个事实,即 promise 类似于仅发出一个值的 observable。
终于看到了来自RxJava的类型信息。 Javascript 不被打字在这里无济于事。基本上,如果 Observable<T>
表示一个压入类型 T 的值的可观察对象,那么 flatMap
将类型 T' -> Observable<T>
的函数作为其参数,并且 returns Observable<T>
。 map
采用 T' -> T
和 returns Observable<T>
类型的函数。
回到您的示例,您有一个从 url 字符串生成承诺的函数。所以 T' : string
,和 T : promise
。而从我们之前所说的promise : Observable<T''>
,所以T : Observable<T''>
,和T'' : html
。如果你把这个 promise producing 函数放在 map
中,当你想要的是 Observable<T''>
时,你会得到 Observable<Observable<T''>>
:你希望 observable 发出 html
值。 flatMap
之所以这样称呼是因为它使 map
的结果变平(删除了一个可观察层)。根据您的背景,这对您来说可能是中文,但是通过输入信息和此处的绘图,一切对我来说都变得 crystal 清楚了:http://reactivex.io/documentation/operators/flatmap.html.
Observable 是一个发出事件流的对象:Next、Error 和 Completed。
当你的函数 returns 一个 Observable 时,它返回的不是一个流,而是一个 Observable 的实例。 flatMap
运算符只是将该实例映射到流。
与 map
相比,这就是 flatMap
的行为:执行给定的函数并将生成的对象展平为流。
['a','b','c'].flatMap(function(e) {
return [e, e+ 'x', e+ 'y', e+ 'z' ];
});
//['a', 'ax', 'ay', 'az', 'b', 'bx', 'by', 'bz', 'c', 'cx', 'cy', 'cz']
['a','b','c'].map(function(e) {
return [e, e+ 'x', e+ 'y', e+ 'z' ];
});
//[Array[4], Array[4], Array[4]]
当你有一个 Observable 的结果是更多的 Observable 时,你可以使用 flatMap。
如果你有一个由另一个可观察对象产生的可观察对象,你不能直接过滤、减少或映射它,因为你有一个可观察对象而不是数据。如果您生成一个可观察对象,请选择 flatMap over map;那你就没事了。
与第二个片段一样,如果您正在进行异步操作,则需要使用 flatMap。
var source = Rx.Observable.interval(100).take(10).map(function(num){
return num+1
});
source.subscribe(function(e){
console.log(e)
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.4.1/Rx.min.js"></script>
var source = Rx.Observable.interval(100).take(10).flatMap(function(num){
return Rx.Observable.timer(100).map(() => num)
});
source.subscribe(function(e){
console.log(e)
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.4.1/Rx.min.js"></script>
用flatMap
var requestStream = Rx.Observable.just('https://api.github.com/users');
var responseMetastream = requestStream
.flatMap(function(requestUrl) {
return Rx.Observable.fromPromise(jQuery.getJSON(requestUrl));
});
responseMetastream.subscribe(json => {console.log(json)})
没有平面地图
var requestStream = Rx.Observable.just('https://api.github.com/users');
var responseMetastream = requestStream
.map(function(requestUrl) {
return Rx.Observable.fromPromise(jQuery.getJSON(requestUrl));
});
responseMetastream.subscribe(jsonStream => {
jsonStream.subscribe(json => {console.log(json)})
})
简单:
[1,2,3].map(x => [x, x * 10])
// [[1, 10], [2, 20], [3, 30]]
[1,2,3].flatMap(x => [x, x * 10])
// [1, 10, 2, 20, 3, 30]]
flatMap
将一个 Observable 发出的项目转换为新的 Observable,然后将这些发出的项目扁平化为单个 Observable。
查看下面的场景,其中 get("posts")
returns 一个 Observable 是 "flattened" by flatMap
。
myObservable.map(e => get("posts")).subscribe(o => console.log(o));
// this would log Observable objects to console.
myObservable.flatMap(e => get("posts")).subscribe(o => console.log(o));
// this would log posts to console.
人们倾向于把事情复杂化,给出的定义是:
flatMap transform the items emitted by an Observable into
Observables, then flatten the emissions from those into a single
Observable
我发誓这个定义仍然让我感到困惑,但我将以最简单的方式解释它,即使用示例
我们的简单示例
1- 我们有一个 Observable,它 return 是一个简单的 URL 字符串。
2- 我们必须使用那个 URL 进行第二个 HTTP 调用。
3- 第二个 HTTP 调用将 return 一个包含我们需要的数据的可观察对象。
所以我们可以想象这样的情况:
Observable 1
|_
Make Http Call Using Observable 1 Data (returns Observable_2)
|_
The Data We Need
如您所见,我们无法直接获取所需的数据
所以要检索数据,我们可以像这样使用普通订阅:
Observable_1.subscribe((URL) => {
Http.get(URL).subscribe((Data_We_Need) => {
console.log(Data_We_Need);
});
});
这是可行的,但如您所见,我们必须嵌套订阅才能获取我们的数据,这目前看起来还不错,但想象一下我们有 10 个嵌套订阅,它们将变得无法维护!
所以处理这个问题的更好方法是使用运算符 flatMap
,它会做同样的事情,但让我们避免嵌套订阅:
Observable_1
.flatMap(URL => Http.get(URL))
.subscribe(Data_We_Need => console.log(Data_We_Need));
此处显示使用订阅的 flatMap 的等效实现。
没有平面地图:
this.searchField.valueChanges.debounceTime(400)
.subscribe(
term => this.searchService.search(term)
.subscribe( results => {
console.log(results);
this.result = results;
}
);
);
使用平面地图:
this.searchField.valueChanges.debounceTime(400)
.flatMap(term => this.searchService.search(term))
.subscribe(results => {
console.log(results);
this.result = results;
});
http://plnkr.co/edit/BHGmEcdS5eQGX703eRRE?p=preview
希望对您有所帮助。
奥利维尔
flatMap transform the items emitted by an Observable into Observables,
then flatten the emissions from those into a single Observable
我不傻,但必须读 10 遍才能理解。
Map 的工作方式类似于数组中每个项目的 for...each 并转换数组中的项目,但保持数组原样:
[1,2,3].map(x => [x, x * 10])
// [[1, 10], [2, 20], [3, 30]]
Flatmap 与 map 的作用相同,但也会“展平”数组:
[1,2,3].flatMap(x => [x, x * 10])
// [1, 10, 2, 20, 3, 30]
flatMap :
- map: transform *) emitted items into Observables.
- flat: then merge those Observables as one Observable.
*) 转换词表示该项目可以转换成其他东西。
然后 merge 运算符变得清晰,它在没有映射的情况下进行展平。为什么不叫它mergeMap呢?似乎还有一个 Alias mergeMap 与 flatMap.
同名
flatMap is used to flatten an array of arrays into a single array.
map simply converts one array to an other array. For example, suppose you have a list of person objects like this:
const friends = [
{name: 'Dave', kids: ['Max', 'Jack']},
{name: 'Max', kids: ['Sam', 'Alex', 'Megan']},
{name: 'Jordan', kids: ['Mason', 'Cameron', 'Kaylin']}
];
但您真正需要的是一组人名(即字符串:[“Dave”、“Max”、“Jordan”])。要将此人数组 object 转换为字符串数组,您首先需要这样定义映射函数:
const mapFunction = p -> p.name;
然后,像这样使用 array.map:
const names = friends.map(mapFunction);
其中 return 个:
["Dave", "Max", "Jordan"]
flatMap 与 map 的相似之处在于您将一个数组转换为另一个数组。但是有一些细微的区别:
首先,map一般是一个one-to-one的东西。映射函数取一 object 入,return 取一 object 出:
p -> p.name
这意味着3个人object会产生3个名字。
另一方面,flatMap 是 one-to-many 的东西。映射函数接受一个 object 而 returns 一个数组输出:
p -> p.kids
最终结果:3 人 objects 将产生 8 个孩子的名字。因此,这段代码:
const mapFunction = p -> p.kids;
const kidNames = friends.flatMap(mapFunction);
将return:
["Max", "Jack", "Sam", "Alex", "Megan", "Mason", "Cameron", "Kaylin"]
我开始使用 RxJS,我不明白为什么在这个例子中我们需要使用像 flatMap
或 concatAll
这样的函数;这里的数组数组在哪里?
var requestStream = Rx.Observable.just('https://api.github.com/users');
var responseMetastream = requestStream
.flatMap(function(requestUrl) {
return Rx.Observable.fromPromise(jQuery.getJSON(requestUrl));
});
responseMetastream.subscribe(url => {console.log(url)})
如果有人能直观地解释发生了什么,那将非常有帮助。
这不是数组的数组。它是可观察对象的可观察对象。
以下returns一个可观察的字符串流。
requestStream
.map(function(requestUrl) {
return requestUrl;
});
While this returns an observable stream of observable stream of json
requestStream
.map(function(requestUrl) {
return Rx.Observable.fromPromise(jQuery.getJSON(requestUrl));
});
flatMap
自动为我们展平 observable 以便我们可以直接观察 json 流
当我开始看Rxjs
时,我也偶然发现了那块石头。对我有帮助的是:
- 来自 reactivex.io 的文档。例如,对于
flatMap
:http://reactivex.io/documentation/operators/flatmap.html - 来自 rxmarbles 的文档:http://rxmarbles.com/。您不会在那里找到
flatMap
,您必须查看mergeMap
(另一个名称)。 - 您遗漏的 Rx 简介:https://gist.github.com/staltz/868e7e9bc2a7b8c1f754。它解决了一个非常相似的例子。特别是它解决了这样一个事实,即 promise 类似于仅发出一个值的 observable。
终于看到了来自RxJava的类型信息。 Javascript 不被打字在这里无济于事。基本上,如果
Observable<T>
表示一个压入类型 T 的值的可观察对象,那么flatMap
将类型T' -> Observable<T>
的函数作为其参数,并且 returnsObservable<T>
。map
采用T' -> T
和 returnsObservable<T>
类型的函数。回到您的示例,您有一个从 url 字符串生成承诺的函数。所以
T' : string
,和T : promise
。而从我们之前所说的promise : Observable<T''>
,所以T : Observable<T''>
,和T'' : html
。如果你把这个 promise producing 函数放在map
中,当你想要的是Observable<T''>
时,你会得到Observable<Observable<T''>>
:你希望 observable 发出html
值。flatMap
之所以这样称呼是因为它使map
的结果变平(删除了一个可观察层)。根据您的背景,这对您来说可能是中文,但是通过输入信息和此处的绘图,一切对我来说都变得 crystal 清楚了:http://reactivex.io/documentation/operators/flatmap.html.
Observable 是一个发出事件流的对象:Next、Error 和 Completed。
当你的函数 returns 一个 Observable 时,它返回的不是一个流,而是一个 Observable 的实例。 flatMap
运算符只是将该实例映射到流。
与 map
相比,这就是 flatMap
的行为:执行给定的函数并将生成的对象展平为流。
['a','b','c'].flatMap(function(e) {
return [e, e+ 'x', e+ 'y', e+ 'z' ];
});
//['a', 'ax', 'ay', 'az', 'b', 'bx', 'by', 'bz', 'c', 'cx', 'cy', 'cz']
['a','b','c'].map(function(e) {
return [e, e+ 'x', e+ 'y', e+ 'z' ];
});
//[Array[4], Array[4], Array[4]]
当你有一个 Observable 的结果是更多的 Observable 时,你可以使用 flatMap。
如果你有一个由另一个可观察对象产生的可观察对象,你不能直接过滤、减少或映射它,因为你有一个可观察对象而不是数据。如果您生成一个可观察对象,请选择 flatMap over map;那你就没事了。
与第二个片段一样,如果您正在进行异步操作,则需要使用 flatMap。
var source = Rx.Observable.interval(100).take(10).map(function(num){
return num+1
});
source.subscribe(function(e){
console.log(e)
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.4.1/Rx.min.js"></script>
var source = Rx.Observable.interval(100).take(10).flatMap(function(num){
return Rx.Observable.timer(100).map(() => num)
});
source.subscribe(function(e){
console.log(e)
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.4.1/Rx.min.js"></script>
用flatMap
var requestStream = Rx.Observable.just('https://api.github.com/users');
var responseMetastream = requestStream
.flatMap(function(requestUrl) {
return Rx.Observable.fromPromise(jQuery.getJSON(requestUrl));
});
responseMetastream.subscribe(json => {console.log(json)})
没有平面地图
var requestStream = Rx.Observable.just('https://api.github.com/users');
var responseMetastream = requestStream
.map(function(requestUrl) {
return Rx.Observable.fromPromise(jQuery.getJSON(requestUrl));
});
responseMetastream.subscribe(jsonStream => {
jsonStream.subscribe(json => {console.log(json)})
})
简单:
[1,2,3].map(x => [x, x * 10])
// [[1, 10], [2, 20], [3, 30]]
[1,2,3].flatMap(x => [x, x * 10])
// [1, 10, 2, 20, 3, 30]]
flatMap
将一个 Observable 发出的项目转换为新的 Observable,然后将这些发出的项目扁平化为单个 Observable。
查看下面的场景,其中 get("posts")
returns 一个 Observable 是 "flattened" by flatMap
。
myObservable.map(e => get("posts")).subscribe(o => console.log(o));
// this would log Observable objects to console.
myObservable.flatMap(e => get("posts")).subscribe(o => console.log(o));
// this would log posts to console.
人们倾向于把事情复杂化,给出的定义是:
flatMap transform the items emitted by an Observable into Observables, then flatten the emissions from those into a single Observable
我发誓这个定义仍然让我感到困惑,但我将以最简单的方式解释它,即使用示例
我们的简单示例
1- 我们有一个 Observable,它 return 是一个简单的 URL 字符串。
2- 我们必须使用那个 URL 进行第二个 HTTP 调用。
3- 第二个 HTTP 调用将 return 一个包含我们需要的数据的可观察对象。
所以我们可以想象这样的情况:
Observable 1
|_
Make Http Call Using Observable 1 Data (returns Observable_2)
|_
The Data We Need
如您所见,我们无法直接获取所需的数据
所以要检索数据,我们可以像这样使用普通订阅:
Observable_1.subscribe((URL) => {
Http.get(URL).subscribe((Data_We_Need) => {
console.log(Data_We_Need);
});
});
这是可行的,但如您所见,我们必须嵌套订阅才能获取我们的数据,这目前看起来还不错,但想象一下我们有 10 个嵌套订阅,它们将变得无法维护!
所以处理这个问题的更好方法是使用运算符 flatMap
,它会做同样的事情,但让我们避免嵌套订阅:
Observable_1
.flatMap(URL => Http.get(URL))
.subscribe(Data_We_Need => console.log(Data_We_Need));
此处显示使用订阅的 flatMap 的等效实现。
没有平面地图:
this.searchField.valueChanges.debounceTime(400)
.subscribe(
term => this.searchService.search(term)
.subscribe( results => {
console.log(results);
this.result = results;
}
);
);
使用平面地图:
this.searchField.valueChanges.debounceTime(400)
.flatMap(term => this.searchService.search(term))
.subscribe(results => {
console.log(results);
this.result = results;
});
http://plnkr.co/edit/BHGmEcdS5eQGX703eRRE?p=preview
希望对您有所帮助。
奥利维尔
flatMap transform the items emitted by an Observable into Observables, then flatten the emissions from those into a single Observable
我不傻,但必须读 10 遍才能理解。
Map 的工作方式类似于数组中每个项目的 for...each 并转换数组中的项目,但保持数组原样:
[1,2,3].map(x => [x, x * 10])
// [[1, 10], [2, 20], [3, 30]]
Flatmap 与 map 的作用相同,但也会“展平”数组:
[1,2,3].flatMap(x => [x, x * 10])
// [1, 10, 2, 20, 3, 30]
flatMap :
- map: transform *) emitted items into Observables.
- flat: then merge those Observables as one Observable.
*) 转换词表示该项目可以转换成其他东西。
然后 merge 运算符变得清晰,它在没有映射的情况下进行展平。为什么不叫它mergeMap呢?似乎还有一个 Alias mergeMap 与 flatMap.
同名flatMap is used to flatten an array of arrays into a single array.
map simply converts one array to an other array. For example, suppose you have a list of person objects like this:
const friends = [
{name: 'Dave', kids: ['Max', 'Jack']},
{name: 'Max', kids: ['Sam', 'Alex', 'Megan']},
{name: 'Jordan', kids: ['Mason', 'Cameron', 'Kaylin']}
];
但您真正需要的是一组人名(即字符串:[“Dave”、“Max”、“Jordan”])。要将此人数组 object 转换为字符串数组,您首先需要这样定义映射函数:
const mapFunction = p -> p.name;
然后,像这样使用 array.map:
const names = friends.map(mapFunction);
其中 return 个:
["Dave", "Max", "Jordan"]
flatMap 与 map 的相似之处在于您将一个数组转换为另一个数组。但是有一些细微的区别: 首先,map一般是一个one-to-one的东西。映射函数取一 object 入,return 取一 object 出:
p -> p.name
这意味着3个人object会产生3个名字。
另一方面,flatMap 是 one-to-many 的东西。映射函数接受一个 object 而 returns 一个数组输出:
p -> p.kids
最终结果:3 人 objects 将产生 8 个孩子的名字。因此,这段代码:
const mapFunction = p -> p.kids;
const kidNames = friends.flatMap(mapFunction);
将return:
["Max", "Jack", "Sam", "Alex", "Megan", "Mason", "Cameron", "Kaylin"]