Neden flatMap kullanmamız gerekiyor?


94

RxJS'yi kullanmaya başlıyorum ve neden bu örnekte flatMapveya gibi bir işlev kullanmamız gerektiğini anlamıyorum concatAll; burada dizi dizisi nerede?

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)})

Birisi ne olduğunu görsel olarak açıklayabilirse, bu çok yardımcı olacaktır.


1
bu cevap, sağladığı değerli referanslar nedeniyle harika, ancak rxjs terminolojisi İngilizceye pek iyi tercüme edilmiyor. (resimler daha iyi). Bu yüzden bunun yerine bunun gibi basit örnekleri çalıştırmanızı veya rxjs deposundaki daha karmaşık örnekleri çalıştırmanızı ve bir flatmap ve harita operatöründen önce ve sonra ".do" operatörleri eklemenizi ve ardından Chrome hata ayıklayıcı ile bir kesme noktası ayarlamanızı öneririm. her birinin farklı bir çıktı ürettiğini anında göreceksiniz
HipsterZipster 03

5
Sanırım flatMapisimlendirilseydi mapThenFlattendaha az kafa karıştırıcı olurdu.
keçi

Yanıtlar:


73

Bakmaya başladığımda Rxjsben de o taşa rastladım. Bana yardımcı olan şey şudur:

  • reactivex.io'dan belgeler. Örneğin, için flatMap: http://reactivex.io/documentation/operators/flatmap.html
  • rxmarbles belgeleri: http://rxmarbles.com/ . flatMapOrada bulamayacaksın , mergeMaponun yerine bakmalısın (başka bir isim).
  • Kaçırdığınız Rx girişi: https://gist.github.com/staltz/868e7e9bc2a7b8c1f754 . Çok benzer bir örneği ele alıyor. Özellikle, bir vaadin, yalnızca bir değer yayan gözlemlenebilir bir vaatle benzer olduğu gerçeğine işaret eder.
  • son olarak RxJava'daki tür bilgilerine bakıyorum. Javascript'in yazılmaması burada yardımcı olmuyor. Temel olarak Observable<T>, T tipi değerleri iten gözlemlenebilir bir nesneyi belirtirse , argüman olarak flatMapbir tür işlevi alır T' -> Observable<T>ve geri döner Observable<T>. maptürden bir işlevi alır T' -> Tve döndürür Observable<T>.

    Örneğinize geri dönersek, bir url dizesinden vaatler üreten bir işleve sahipsiniz. Yani T' : stringve T : promise. Ve Neyden Daha önce de söylediğimiz promise : Observable<T''>bu yüzden, T : Observable<T''>birlikte, T'' : html. Bu vaat üreten işlevi koyarsanız map, istediğiniz şeyi elde Observable<Observable<T''>>edersiniz Observable<T''>: gözlemlenebilir olanın htmldeğerleri yaymasını istersiniz . flatMapböyle denir çünkü sonucu düzleştirir (gözlenebilir bir katmanı kaldırır) map. Geçmişinize bağlı olarak, bu sizin için Çince olabilir, ancak buradan bilgi ve çizim yazarken her şey benim için kristal netliğinde oldu: http://reactivex.io/documentation/operators/flatmap.html .


2
Sana basitleştirmek mümkün olmalıdır belirtmeyi unutmuşum return Rx.Observable.fromPromise(jQuery.getJSON(requestUrl));için return jQuery.getJSON(requestUrl);olduğu gibi flatMapaynı zamanda türde bir söz yani fonksiyonunu döndüren bir selektör işlevini kabul eder T' -> Promise.
user3743222

2
Vay canına, GitHub Gist ( gist.github.com/staltz/868e7e9bc2a7b8c1f754 ) kanlı bir harika. RxJS gibi herhangi bir ReactiveX kitaplığı ile çalışan herkese tavsiye ederim.
Jacob Stamm

@JacobStamm katılıyorum. İşleri kolaylaştırır.
CruelEngine

Bu sözdizimi ne anlama geliyor T’ -> T? ' TYi genel olarak anlıyorum , ancak kesme işareti ve yağsız ok nedir?
1252748

Yanıtın herhangi bir yerinde anlamı değiştirmeden T 'yi X veya Y ile değiştirebilirsiniz. Ok, tip imzası için Haskell gösterimidir. Öyleyse T '-> T, T' tipinde bir eleman alan ve T tipi bir eleman döndüren bir fonksiyonun
imzasıdır

124
['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]]

Sonuçları daha fazla Gözlemlenebilir olan bir Gözlemlenebilir'e sahip olduğunuzda flatMap'i kullanırsınız.

Başka bir gözlemlenebilir tarafından üretilen bir gözlemlenebiliriniz varsa, onu doğrudan filtreleyemez, azaltamaz veya haritalayamazsınız çünkü veriye değil bir Gözlemlenebilir'e sahipsiniz. Gözlenebilir bir harita üretirseniz, harita üzerinde flatMap'i seçin; o zaman iyisin.

İkinci kod parçacığında olduğu gibi, eşzamansız işlem yapıyorsanız, flatMap'i kullanmanız gerekir.

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>


34

İnsanlar şu tanımları vererek işleri karmaşıklaştırma eğilimindedir :

flatMap, bir Gözlemlenebilir tarafından yayılan öğeleri Gözlemlenebilirlere dönüştürür, ardından bunlardan gelen emisyonları tek bir Gözlenebilir

Yemin ederim bu tanım hala kafamı karıştırıyor ama bunu en basit şekilde bir örnekle açıklayacağım

Durumumuz : İhtiyacımız olan verileri içeren bir gözlemlenebilir döndüren bir HTTP çağrısı yapmak için kullanacağımız verileri (basit URL) döndüren bir gözlemlenebilirimiz var, böylece durumu şu şekilde görselleştirebilirsiniz:

Observable 1
    |_
       Make Http Call Using Observable 1 Data (returns Observable_2)
            |_
               The Data We Need

Gördüğünüz gibi ihtiyacımız olan verilere doğrudan erişemiyoruz, bu nedenle verileri almanın ilk yolu, bunun gibi normal abonelikleri kullanabiliriz:

Observable_1.subscribe((URL) => {
         Http.get(URL).subscribe((Data_We_Need) => {
                  console.log(Data_We_Need);
          });
});

bu işe yarıyor, ancak görebileceğiniz gibi, verilerimizi almak için abonelikleri iç içe yerleştirmemiz gerekiyor, bu şu anda kötü görünmüyor, ancak sürdürülemez hale gelebilecek 10 iç içe geçmiş aboneliğimiz olduğunu hayal edin.

Bu nedenle, bunun üstesinden gelmenin daha iyi bir yolu flatMap, aynı şeyi yapacak ancak iç içe geçmiş abonelikten kaçınmamızı sağlayan operatörü kullanmaktır :

Observable_1
    .flatMap(URL => Http.get(URL))
    .subscribe(Data_We_Need => console.log(Data_We_Need));

32

flatMap Bir Gözlemlenebilir tarafından yayılan öğeleri yeni Gözlenebilirlere dönüştürür, ardından bunlardan çıkan emisyonları tek bir Gözlenebilir Öğeye düzleştirir.

get("posts")Tarafından "düzleştirilmiş" bir Gözlenebilirlik döndüren aşağıdaki senaryoyu inceleyin 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.

2
Güzel, basit cevap. Sanırım bu en iyisi olabilir.
vaughan

"flatMap, bir Gözlemlenebilir tarafından yayılan öğeleri yeni Gözlemlenebilirlere dönüştürür, ardından bunlardan gelen emisyonları tek bir Gözlemlenebilir'e düzleştirir." Bu harika bir şey.
MBak

18

Basit:

[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]]

16

Bir dizi dizisi değil. Gözlenebilirlerin bir gözlemlenebilir.

Aşağıdaki, gözlemlenebilir bir dize akışı döndürür.

requestStream
  .map(function(requestUrl) {
    return requestUrl;
  });

Bu, gözlemlenebilir bir json akışını döndürürken

requestStream
  .map(function(requestUrl) {
    return Rx.Observable.fromPromise(jQuery.getJSON(requestUrl));
  });

flatMap bizim için gözlemlenebilir olanı otomatik olarak düzleştirir, böylece json akışını doğrudan gözlemleyebiliriz


3
Bu kavramı anlamak zordur, lütfen "gözlemlenebilir bir json akışı gözlemlenebilir akışı döndürür" demek istediğinizi görsele yorumlar ekleyebilir misiniz? Teşekkürler.
user233232

@ user233232, örneğin [x, x, x, x] - [[xxx], [[xxx], [xxx]]]
serkan

İlk cümleyi anlamanın anahtarı, bunun flatMap(ve map) dizilere özel olmadığını anlamaktır. Bu işlemleri diziler, sözlükler, "isteğe bağlı ifadeler", reaktif akışlar, vaatler, işaretçiler ve hatta işlevlerin kendileri dahil olmak üzere herhangi bir genel kapsayıcı veya sarmalayıcı üzerinde tanımlamak mümkündür. Bu, monad adı verilen matematiksel bir yapının ortaya çıkan bir özelliğidir. Yukarıdaki tüm örnekler bir monad olma gereksinimlerini karşılar ve bu nedenle hepsine bir tanım mapve a flatMap(bazı uyarılarla birlikte) verilebilir .
mklbtz

14

Burada, aboneleri kullanan bir flatMap'in eşdeğer uygulamasını göstermek için.

FlatMap olmadan:

this.searchField.valueChanges.debounceTime(400)
.subscribe(
  term => this.searchService.search(term)
  .subscribe( results => {
      console.log(results);  
      this.result = results;
    }
  );
);

FlatMap ile:

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

Umarım yardımcı olabilir.

Olivier.


13

Gözlemlenebilir, bir olay akışı yayan bir nesnedir: Sonraki, Hata ve Tamamlandı.

İşleviniz bir Gözlemlenebilir döndürdüğünde, bir akış değil, bir Gözlemlenebilir örneği döndürür. flatMapOperatör sadece akışa bu örneği eşler.

Aşağıdakilerle flatMapkarşılaştırıldığında davranış budur map: Verilen işlevi çalıştırın ve elde edilen nesneyi bir akış halinde düzleştirin.


7

FlatMap ile

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)})

FlatMap olmadan

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)})
})

0

flatMap, bir Gözlemlenebilir tarafından yayılan öğeleri Gözlemlenebilirlere dönüştürür, ardından bunlardan gelen emisyonları tek bir Gözlenebilir

Aptal değilim ama bunu 10 kez okumak zorunda kaldım ve hala anlamıyorum. Kod parçacığını okuduğumda:

[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]

sonra ne olduğunu anlayabilirim, iki şey yapar:

flatMap :

  1. map : yayılan öğeleri Gözlemlenebilirlere dönüştürün.
  2. flat : daha sonra bu Gözlemlenebilirleri bir Gözlenebilir olarak birleştirin.

*) Dönüşüm kelimesi, öğenin başka bir şeye dönüştürülebileceğini söylüyor.

Ardından birleştirme operatörü anlaşılır hale gelir, eşleştirme yapmadan düzleştirmeyi yapar. Neden ona mergeMap demiyorsunuz ? Bir Alias da var gibi görünüyor mergeMap için bu adla flatMap .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.