1

1. What's going on

I have some data that I need to map into the right format to be useful for what I want to do. The data I get can also vary in length (means, it could be one object, or 10, or 200). However – I only ever care about the first 6 elements within that array. When there are only e.g. 3 elements, then I still want to emit the result. But I do NOT want to emit a change when the values didn't change (the source could trigger an update, but the data could still be the same).

The data I may get looks like this:

var data = {
  myData: {
    cats: [             // data I care about!
      {
        name: 'Mr. Cat',
        someReallyLongPropertyNameThatLinksToAnImage: 'http://cat/ur/day.jpg',
        ... // SOME MORE PROPS
      },
      {
        name: 'Supercat',
        someReallyLongPropertyNameThatLinksToAnImage: 'http://cat/ur/day2.jpg',
        ... // SOME MORE PROPS
      },
      ... etc.
    ]  
  },
  foo: {                // data I don't care about...
    bar: [{...}...]     // data I don't care about...
  },
  baz: {
    butz: [{...}...]    // data I don't care about...
  }
}

The result I want should look like this:

var result = [
  {
    image: 'http://url/to/1',
    title: 'title 1'
  },
  {
    image: 'http://url/to/2',
    title: 'title 2'
  },
  {
    image: 'http://url/to/3',
    title: 'title 3'
  }
]

2. What's the problem?

My problem is, that I don't know how to:

  • emit a change with n items (don't emit PER item)

    It looks like bufferWithCount(6) is some sort of what I am looking for, but that doesn't work when there are only 3 elements within that array!

  • only emit a change when the resulting array is different

    when the result looks exactly the same as before, then don't trigger a change event.

3. What I did (but doesn't work obviously)

Rx.Observable.of(data)
  .map((e) => data.myStuff.cats)
  .map((arr) => {
    // this would emit a change for EACH element, right? ugh.
    return Rx.Observable.fromArray(arr)
      // only care about the first 6 elements? if it only
      // has 3, would this still work?
      .take(6)
      // pluck out the props I need. (maybe not neccessary, but anyway)
      .pluck('someReallyLongPropertyNameThatLinksToAnImage', 'name') 
      .map((el) => {
        // map the data into the right format
        return {
          title: el.name
          image: el.someReallyLongPropertyNameThatLinksToAnImage
        }
      })
  })
  .distinctUntilChanged() // yeah... this doesn't work either I guess..
  .subscribe(
    (result) => console.log(result)
  )
Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Martin Broder
  • 221
  • 3
  • 14

1 Answers1

1

I think it is probably easiest to just use the Array.prototype.slice method instead of trying to hack a solution with Rx.

As for using distinctUntilChanged you will need to use a comparer function to tell it how to compare the two arrays I imagine:

Rx.Observable.just(data)
  //Arrays slice will only take up to six items from the array 
  .map((e) => e.myData.cats.slice(0, 6))
  //Only updates if the array values have changed
  .distinctUntilChanged(null, (cur, next) => /*Implement your array value comparison*/)
  //Converts the values into something we can work with
  .map((arr) => arr.map(el => { 
                         return {name : el.name, 
                                 image : el.someBlahBlah}; 
                })
  )
  //Receives a series of arrays only if the values have changed.
  .subscribe();

As to how you would do the array comparison that is another matter entirely and is really up to you but you can see this answer here.

Essentially though, the simplest solution would be to iterate through the arrays and check if the fields in the objects differ here is slightly modified code from the link:

// attach the .equals method to Array's prototype to call it on any array
Array.prototype.equals = function (array) {
  // if the other array is a falsy value, return
  if (!array)
      return false;

  // compare lengths - can save a lot of time 
  if (this.length != array.length)
      return false;

  for (var i = 0, l=this.length; i < l; i++) {
      //Compare properties here, 
      //just make sure you are comparing value instances not references     
      if (this[i].name != array[i].name) { 
          // Warning - two different object instances will never be equal: {x:20} != {x:20}
          return false;   
      }           
  }       
  return true;
}
Community
  • 1
  • 1
paulpdaniels
  • 18,395
  • 2
  • 51
  • 55