web-dev-qa-db-fra.com

Comment collecter un tableau de valeurs émises depuis Observable.from?

Donc, dans Rxjs, j'ai un tas de code,

return Observable.from(input_array)
           .concatMap((item)=>{
               //this part emits an Observable.of<string> for each item in the input_array
           })
           .scan((output_array:string[],each_item_output_array:string)=>{
               return output_array.Push(each_item_output_array) ;
           });

Mais apparemment, c'est faux, l'analyse va casser le code à l'intérieur du concatMap, donc je veux savoir comment collecter le tableau de sortie de chaque élément dans l'opérateur from observable?

9
tomriddle_1234

Dans votre appel à scan vous n'avez pas spécifié de valeur de départ pour l'accumulateur. Dans ce cas, la première valeur est utilisée comme graine. Par exemple:

Rx.Observable
  .from(["a", "b", "c"])
  .scan((acc, value) => acc + value)
  .subscribe(value => console.log(value));
<script src="https://unpkg.com/rxjs@5/bundles/Rx.min.js"></script>

Dans votre extrait de code, la première valeur n'est pas un tableau, vous ne pouvez donc pas appeler Push dessus. Pour accumuler les valeurs dans un tableau, vous pouvez spécifier une graine de tableau comme ceci:

Rx.Observable
  .from(["a", "b", "c"])
  .concatMap(value => Rx.Observable.of(value))
  .scan((acc, value) => {
    acc.Push(value);
    return acc;
  }, []) // Note that an empty array is use as the seed
  .subscribe(value => console.log(JSON.stringify(value)));
<script src="https://unpkg.com/rxjs@5/bundles/Rx.min.js"></script>

Bien que, dans certains cas d'utilisation, il soit préférable de ne pas muter le tableau:

Rx.Observable
  .from(["a", "b", "c"])
  .concatMap(value => Rx.Observable.of(value))
  .scan((acc, value) => [...acc, value], [])
  .subscribe(value => console.log(JSON.stringify(value)));
<script src="https://unpkg.com/rxjs@5/bundles/Rx.min.js"></script>

Notez que scan émet un tableau pour chaque valeur qu'il reçoit. Si vous ne souhaitez émettre qu'un seul tableau à la fin de l'observable, vous pouvez utiliser l'opérateur toArray à la place:

Rx.Observable
  .from(["a", "b", "c"])
  .concatMap(value => Rx.Observable.of(value))
  .toArray()
  .subscribe(value => console.log(JSON.stringify(value)));
<script src="https://unpkg.com/rxjs@5/bundles/Rx.min.js"></script>
31
cartant

Une autre option est bufferCount(count) si vous connaissez la longueur du tableau d'entrée, vous pouvez obtenir une seule sortie contenant ce nombre d'éléments. Une syntaxe plus propre que de devoir se rappeler comment utiliser scan.

Remarque: Si vous ne connaissez pas la taille (bien que dans votre exemple, vous le fassiez), alors count représente un maximum - mais cela peut avoir des contraintes de ressources, alors ne faites pas seulement 99999.

 const array = source$.pipe(bufferCount(10));

Dans mon cas, j'avais une liste "d'opérations" en cours d'exécution et je connaissais le nombre total d'étapes à l'avance, donc bufferCount fonctionnait assez bien. Cependant, assurez-vous de bien considérer les conditions d'erreur

 // one approach to handling errors that still returns an output array
 // only use this if a single failure shouldn't stop the collection
 const array = source$.pipe(
                    map((result) => ({ hasError: false, result: result }),
                    catchError((err) => ({ hasError: true, error: err }),
                    bufferCount(10));

La décision sur la façon de gérer les erreurs variera considérablement en fonction de ce que vos observables sont réellement - mais le point ici est de montrer bufferCount() en option.

(D'autres opérations buffer sont également disponibles)

1
Simon_Weaver

Attention à ce code:

      const obs = Rx.Observable
      .from(["a", "b", "c"])
      .concatMap(value => Rx.Observable.of(value))
      .scan((acc, value) => {
        acc.Push(value);
        return acc;
      }, []); 
      obs.subscribe(value => console.log(JSON.stringify(value)));
      obs.subscribe(value => console.log(JSON.stringify(value)));

Le résultat sera un peu inattendu:

 ["a"]
 ["a","b"]
 ["a","b","c"]
 ["a","b","c","a"]
 ["a","b","c","a","b"]
 ["a","b","c","a","b","c"]

La variable "acc" est un objet de référence et chaque abonné obtient des données de flux et ajoute à nouveau des données au même objet. Il peut y avoir beaucoup de solutions pour l'éviter, c'est la création d'un nouvel objet lorsque les données de flux sont à nouveau reçues:

    var obs = Rx.Observable
          .from(["a", "b", "c"])
          .concatMap(value => Rx.Observable.of(value))
          .scan((acc, value) => {
          //clone initial value
            if (acc.length == 0) {

                   acc = [];
                }
            acc.Push(value);
            return acc 
          }, []); // Note that an empty array is use as the seed
          obs.subscribe(value => console.log(JSON.stringify(value)));
          obs.subscribe(value => console.log(JSON.stringify(value)));

résultat comme prévu:

 ["a"]
 ["a","b"]
 ["a","b","c"]
 ["a"]
 ["a","b"]
 ["a","b","c"]

J'espère que cela fait gagner beaucoup de temps à quelqu'un

0
Oleg Bondarenko