Bug with observable.map()

Each time I call :

function filterShows(sort) {
    console.log(showsList.length);
    var filtered = showsList.map(function(item) {
        return item;
    });
    shows.refreshAll(filtered);
}

I get :

LOG: 22
LOG: 11
LOG: 5
LOG: 2
LOG: 1

but

function filterShows(sort) {
    console.log(showsList.length);
    var filtered = showsList;
    shows.refreshAll(filtered);
}

gives

LOG: 22
LOG: 22
LOG: 22
LOG: 22
LOG: 22
LOG: 22

Any advice ? I’m quite stuck with this.

Hey, and thanks for your report!

That does indeed look surprising, but it’s hard to say what’s wrong just from those code snippets. Can you provide us what we need to reproduce the problem?

Cheers!

Sure please see my repository.

Just click on the header tabs to call this function.

You can comment showsList.map() to see the different behavior I explained in my bug report.

Hey again Gaëtan,

On this line you assign shows = showsList; which means that the two variables refer to the same observable. Given that, what you’re seeing seems like the correct behaviour to me.

Hope that helps!

Isn’t that line supposed to be interpreted only once ? I’m quite confused on how I should init the shows observable.

Yes, that line should only be interpreted once. Note that the meaning of the line is something like "shows now refers to the same observable that showsList refers to". The meaning is not "copy the elements from showsList into shows" which I gather is what you want. Remember also that showsList is initialised asynchronously since you’re using fetch, so at that point in time showsList might not be filled anyway. Perhaps you can initialize both shows and showsList at the same time?

I followed your advice, as I did not get that the assignment makes the two variables reffering t the same observable.

But I still think something is wrong with map() as I still have the same issue with values disapearing from the original observable while a simple assignement is fine.

Even with :

filtered = showsList.map(function(item) {
    return 'test';
});

```

The result is just the same !

Hey Gaëtan!

It would help if you were able to provide a description of what you want to achieve and maybe even a boiled-down example of what you tried.

I’m guessing a bit here, but I think you have an observable list of shows and want to display a list filtered according to some criteria. I cooked up a little example of how that can be achieved here:

<App>
  <JavaScript>
    var Observable = require("FuseJS/Observable");
    var shows = Observable("Game of Phones", "Desperate Houseknives", "Twin Freaks", "House of Yards");
    var filterString = Observable("");
    var filter = function(x) {
      return Observable(function() {
        var str = filterString.value;
        if (str === "") {
          return true;
        }
        else {
          return x.indexOf(str) >= 0;
        }
      });
    };
    var filteredShows = shows.where(filter);
    module.exports = {
      filterString: filterString,
      filteredShows: filteredShows,
    };
  </JavaScript>
  <StackPanel>
    <TextInput PlaceholderText="Filter" Value="{filterString}" />
    <Text>Shows:</Text>
    <Each Items="{filteredShows}">
      <Text Value="{}" />
    </Each>
  </StackPanel>
</App>

Here we have a filterString that is bound to the text of an input field. filteredShows is then the observable that you get by doing shows.where(filter), where filter is a function that takes a show name and returns an observable bool. The reason for returning an observable is to make the condition react to changes in the filterString (note that obs.where(cond) will react to changes not only to obs but also to cond if cond returns an observable). In this case the condition is that if the filter string is empty, we always return true, i.e. don’t filter anything, and if the string is non-empty, we return all shows whose name includes the filter string.

Cheers, and hope that helps! :slight_smile: