How can I give an RxJS observable pipe access to the original observable's emission AND the pipe's previous emission?









up vote
1
down vote

favorite












I have an RxJS Observable that emits a series of changes to an underlying data structure—specifically, snapshotChanges() from an AngularFirestoreCollection.



  • I'm currently mapping this to an array of plain JavaScript objects for consumption by my app.

  • This array is not protected in any way, and consuming code could accidentally modify this structure.

  • The entire array is rebuilt whenever the underlying data source emits, even if only one (or sometimes no) item in the array has actually changed.

  • Because of this, all references change each time, making change detection harder than it needs to be—and really slowing down my app.

What I want to do instead is use Immer to maintain an immutable structure, such that unchanged data is structurally shared with the “new” array.



What I can't work out is how to pipe() off the snapshotChanges() observable such that the pipe gets access to the previously emitted immutable data (or a first-time default) in addition to the latest snapshotChanges() output.



In code, what I basically already have is this:



const docToObject = (doc) => /* change document to fresh plain object every time */ ;
const mappedData$ = snapshotChanges().pipe(
map(changes => changes.map(change => docToObject(change.payload.doc)),
tap(array => console.log('mutable array:', array)),
);


and I'm essentially looking for something like this, where I don't know what XXX(...) should be:



const newImmutableObject = (changes, old) => 
// new immutable structure from old one + changes, structurally sharing as much as
// possible
;
const mappedData$ = snapshotChanges().pipe(

// ==================================================================================
XXX(...), // missing ingredient to combine snapshotChanges and previously emitted
// value, or default to
// ==================================================================================

map(([snapshotChanges, prevImmutableOutput]) => newImmutableOutput(...)),
tap(array => console.log('IMMUTABLE ARRAY with shared structure:', array)),
);


I feel like the expand operator is close to what I need, but it seems to only pass the previously emitted value in on subsequent runs, whereas I also need the newly emitted snapshotChanges.



Given an RxJS Observable pipe, how can I operate on this Observable's emissions while also having access to the pipe's previous emission?










share|improve this question

















  • 3




    scan seems to be what you are looking for: snapshotChanges().pipe(scan((prev, changes) => newImmutableObject(changes, prev), ))
    – cartant
    Nov 10 at 4:47











  • Thank you—this is exactly what I was looking for (I was so close!). The learn-rxjs page for scan even says “You can create Redux-like state management with scan!” Example 2: Accumulating an object shows almost precisely what I'm trying to achieve.
    – Alex Peters
    Nov 10 at 14:11














up vote
1
down vote

favorite












I have an RxJS Observable that emits a series of changes to an underlying data structure—specifically, snapshotChanges() from an AngularFirestoreCollection.



  • I'm currently mapping this to an array of plain JavaScript objects for consumption by my app.

  • This array is not protected in any way, and consuming code could accidentally modify this structure.

  • The entire array is rebuilt whenever the underlying data source emits, even if only one (or sometimes no) item in the array has actually changed.

  • Because of this, all references change each time, making change detection harder than it needs to be—and really slowing down my app.

What I want to do instead is use Immer to maintain an immutable structure, such that unchanged data is structurally shared with the “new” array.



What I can't work out is how to pipe() off the snapshotChanges() observable such that the pipe gets access to the previously emitted immutable data (or a first-time default) in addition to the latest snapshotChanges() output.



In code, what I basically already have is this:



const docToObject = (doc) => /* change document to fresh plain object every time */ ;
const mappedData$ = snapshotChanges().pipe(
map(changes => changes.map(change => docToObject(change.payload.doc)),
tap(array => console.log('mutable array:', array)),
);


and I'm essentially looking for something like this, where I don't know what XXX(...) should be:



const newImmutableObject = (changes, old) => 
// new immutable structure from old one + changes, structurally sharing as much as
// possible
;
const mappedData$ = snapshotChanges().pipe(

// ==================================================================================
XXX(...), // missing ingredient to combine snapshotChanges and previously emitted
// value, or default to
// ==================================================================================

map(([snapshotChanges, prevImmutableOutput]) => newImmutableOutput(...)),
tap(array => console.log('IMMUTABLE ARRAY with shared structure:', array)),
);


I feel like the expand operator is close to what I need, but it seems to only pass the previously emitted value in on subsequent runs, whereas I also need the newly emitted snapshotChanges.



Given an RxJS Observable pipe, how can I operate on this Observable's emissions while also having access to the pipe's previous emission?










share|improve this question

















  • 3




    scan seems to be what you are looking for: snapshotChanges().pipe(scan((prev, changes) => newImmutableObject(changes, prev), ))
    – cartant
    Nov 10 at 4:47











  • Thank you—this is exactly what I was looking for (I was so close!). The learn-rxjs page for scan even says “You can create Redux-like state management with scan!” Example 2: Accumulating an object shows almost precisely what I'm trying to achieve.
    – Alex Peters
    Nov 10 at 14:11












up vote
1
down vote

favorite









up vote
1
down vote

favorite











I have an RxJS Observable that emits a series of changes to an underlying data structure—specifically, snapshotChanges() from an AngularFirestoreCollection.



  • I'm currently mapping this to an array of plain JavaScript objects for consumption by my app.

  • This array is not protected in any way, and consuming code could accidentally modify this structure.

  • The entire array is rebuilt whenever the underlying data source emits, even if only one (or sometimes no) item in the array has actually changed.

  • Because of this, all references change each time, making change detection harder than it needs to be—and really slowing down my app.

What I want to do instead is use Immer to maintain an immutable structure, such that unchanged data is structurally shared with the “new” array.



What I can't work out is how to pipe() off the snapshotChanges() observable such that the pipe gets access to the previously emitted immutable data (or a first-time default) in addition to the latest snapshotChanges() output.



In code, what I basically already have is this:



const docToObject = (doc) => /* change document to fresh plain object every time */ ;
const mappedData$ = snapshotChanges().pipe(
map(changes => changes.map(change => docToObject(change.payload.doc)),
tap(array => console.log('mutable array:', array)),
);


and I'm essentially looking for something like this, where I don't know what XXX(...) should be:



const newImmutableObject = (changes, old) => 
// new immutable structure from old one + changes, structurally sharing as much as
// possible
;
const mappedData$ = snapshotChanges().pipe(

// ==================================================================================
XXX(...), // missing ingredient to combine snapshotChanges and previously emitted
// value, or default to
// ==================================================================================

map(([snapshotChanges, prevImmutableOutput]) => newImmutableOutput(...)),
tap(array => console.log('IMMUTABLE ARRAY with shared structure:', array)),
);


I feel like the expand operator is close to what I need, but it seems to only pass the previously emitted value in on subsequent runs, whereas I also need the newly emitted snapshotChanges.



Given an RxJS Observable pipe, how can I operate on this Observable's emissions while also having access to the pipe's previous emission?










share|improve this question













I have an RxJS Observable that emits a series of changes to an underlying data structure—specifically, snapshotChanges() from an AngularFirestoreCollection.



  • I'm currently mapping this to an array of plain JavaScript objects for consumption by my app.

  • This array is not protected in any way, and consuming code could accidentally modify this structure.

  • The entire array is rebuilt whenever the underlying data source emits, even if only one (or sometimes no) item in the array has actually changed.

  • Because of this, all references change each time, making change detection harder than it needs to be—and really slowing down my app.

What I want to do instead is use Immer to maintain an immutable structure, such that unchanged data is structurally shared with the “new” array.



What I can't work out is how to pipe() off the snapshotChanges() observable such that the pipe gets access to the previously emitted immutable data (or a first-time default) in addition to the latest snapshotChanges() output.



In code, what I basically already have is this:



const docToObject = (doc) => /* change document to fresh plain object every time */ ;
const mappedData$ = snapshotChanges().pipe(
map(changes => changes.map(change => docToObject(change.payload.doc)),
tap(array => console.log('mutable array:', array)),
);


and I'm essentially looking for something like this, where I don't know what XXX(...) should be:



const newImmutableObject = (changes, old) => 
// new immutable structure from old one + changes, structurally sharing as much as
// possible
;
const mappedData$ = snapshotChanges().pipe(

// ==================================================================================
XXX(...), // missing ingredient to combine snapshotChanges and previously emitted
// value, or default to
// ==================================================================================

map(([snapshotChanges, prevImmutableOutput]) => newImmutableOutput(...)),
tap(array => console.log('IMMUTABLE ARRAY with shared structure:', array)),
);


I feel like the expand operator is close to what I need, but it seems to only pass the previously emitted value in on subsequent runs, whereas I also need the newly emitted snapshotChanges.



Given an RxJS Observable pipe, how can I operate on this Observable's emissions while also having access to the pipe's previous emission?







angular rxjs angularfire2 rxjs-pipeable-operators immer.js






share|improve this question













share|improve this question











share|improve this question




share|improve this question










asked Nov 10 at 2:58









Alex Peters

807515




807515







  • 3




    scan seems to be what you are looking for: snapshotChanges().pipe(scan((prev, changes) => newImmutableObject(changes, prev), ))
    – cartant
    Nov 10 at 4:47











  • Thank you—this is exactly what I was looking for (I was so close!). The learn-rxjs page for scan even says “You can create Redux-like state management with scan!” Example 2: Accumulating an object shows almost precisely what I'm trying to achieve.
    – Alex Peters
    Nov 10 at 14:11












  • 3




    scan seems to be what you are looking for: snapshotChanges().pipe(scan((prev, changes) => newImmutableObject(changes, prev), ))
    – cartant
    Nov 10 at 4:47











  • Thank you—this is exactly what I was looking for (I was so close!). The learn-rxjs page for scan even says “You can create Redux-like state management with scan!” Example 2: Accumulating an object shows almost precisely what I'm trying to achieve.
    – Alex Peters
    Nov 10 at 14:11







3




3




scan seems to be what you are looking for: snapshotChanges().pipe(scan((prev, changes) => newImmutableObject(changes, prev), ))
– cartant
Nov 10 at 4:47





scan seems to be what you are looking for: snapshotChanges().pipe(scan((prev, changes) => newImmutableObject(changes, prev), ))
– cartant
Nov 10 at 4:47













Thank you—this is exactly what I was looking for (I was so close!). The learn-rxjs page for scan even says “You can create Redux-like state management with scan!” Example 2: Accumulating an object shows almost precisely what I'm trying to achieve.
– Alex Peters
Nov 10 at 14:11




Thank you—this is exactly what I was looking for (I was so close!). The learn-rxjs page for scan even says “You can create Redux-like state management with scan!” Example 2: Accumulating an object shows almost precisely what I'm trying to achieve.
– Alex Peters
Nov 10 at 14:11












1 Answer
1






active

oldest

votes

















up vote
1
down vote



accepted










As per your requirement I would suggest to use scan operator which can track all previous state and new state.



const newImmutableObject = (changes, old) => 
// new immutable structure from old one + changes, structurally sharing as much as
// possible
;
const mappedData$ = snapshotChanges().pipe(
scan((acc, current) => [...acc, current], ), //<-- scan is used here
map(([snapshotChanges, prevImmutableOutput]) => newImmutableOutput(...)),
tap(array => console.log('IMMUTABLE ARRAY with shared structure:', array)),
);





share|improve this answer




















  • Thank you. In fact, with the scan operator I don't actually need the subsequent map operator after all.
    – Alex Peters
    Nov 10 at 14:13










Your Answer






StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");

StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);

else
createEditor();

);

function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);



);













draft saved

draft discarded


















StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53235649%2fhow-can-i-give-an-rxjs-observable-pipe-access-to-the-original-observables-emiss%23new-answer', 'question_page');

);

Post as a guest















Required, but never shown

























1 Answer
1






active

oldest

votes








1 Answer
1






active

oldest

votes









active

oldest

votes






active

oldest

votes








up vote
1
down vote



accepted










As per your requirement I would suggest to use scan operator which can track all previous state and new state.



const newImmutableObject = (changes, old) => 
// new immutable structure from old one + changes, structurally sharing as much as
// possible
;
const mappedData$ = snapshotChanges().pipe(
scan((acc, current) => [...acc, current], ), //<-- scan is used here
map(([snapshotChanges, prevImmutableOutput]) => newImmutableOutput(...)),
tap(array => console.log('IMMUTABLE ARRAY with shared structure:', array)),
);





share|improve this answer




















  • Thank you. In fact, with the scan operator I don't actually need the subsequent map operator after all.
    – Alex Peters
    Nov 10 at 14:13














up vote
1
down vote



accepted










As per your requirement I would suggest to use scan operator which can track all previous state and new state.



const newImmutableObject = (changes, old) => 
// new immutable structure from old one + changes, structurally sharing as much as
// possible
;
const mappedData$ = snapshotChanges().pipe(
scan((acc, current) => [...acc, current], ), //<-- scan is used here
map(([snapshotChanges, prevImmutableOutput]) => newImmutableOutput(...)),
tap(array => console.log('IMMUTABLE ARRAY with shared structure:', array)),
);





share|improve this answer




















  • Thank you. In fact, with the scan operator I don't actually need the subsequent map operator after all.
    – Alex Peters
    Nov 10 at 14:13












up vote
1
down vote



accepted







up vote
1
down vote



accepted






As per your requirement I would suggest to use scan operator which can track all previous state and new state.



const newImmutableObject = (changes, old) => 
// new immutable structure from old one + changes, structurally sharing as much as
// possible
;
const mappedData$ = snapshotChanges().pipe(
scan((acc, current) => [...acc, current], ), //<-- scan is used here
map(([snapshotChanges, prevImmutableOutput]) => newImmutableOutput(...)),
tap(array => console.log('IMMUTABLE ARRAY with shared structure:', array)),
);





share|improve this answer












As per your requirement I would suggest to use scan operator which can track all previous state and new state.



const newImmutableObject = (changes, old) => 
// new immutable structure from old one + changes, structurally sharing as much as
// possible
;
const mappedData$ = snapshotChanges().pipe(
scan((acc, current) => [...acc, current], ), //<-- scan is used here
map(([snapshotChanges, prevImmutableOutput]) => newImmutableOutput(...)),
tap(array => console.log('IMMUTABLE ARRAY with shared structure:', array)),
);






share|improve this answer












share|improve this answer



share|improve this answer










answered Nov 10 at 9:41









Sunil Singh

5,6161625




5,6161625











  • Thank you. In fact, with the scan operator I don't actually need the subsequent map operator after all.
    – Alex Peters
    Nov 10 at 14:13
















  • Thank you. In fact, with the scan operator I don't actually need the subsequent map operator after all.
    – Alex Peters
    Nov 10 at 14:13















Thank you. In fact, with the scan operator I don't actually need the subsequent map operator after all.
– Alex Peters
Nov 10 at 14:13




Thank you. In fact, with the scan operator I don't actually need the subsequent map operator after all.
– Alex Peters
Nov 10 at 14:13

















draft saved

draft discarded
















































Thanks for contributing an answer to Stack Overflow!


  • Please be sure to answer the question. Provide details and share your research!

But avoid


  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.

To learn more, see our tips on writing great answers.





Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


Please pay close attention to the following guidance:


  • Please be sure to answer the question. Provide details and share your research!

But avoid


  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.

To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53235649%2fhow-can-i-give-an-rxjs-observable-pipe-access-to-the-original-observables-emiss%23new-answer', 'question_page');

);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

Use pre created SQLite database for Android project in kotlin

Darth Vader #20

Ondo