0

I have a really large collection, 2000++ items, of images in SVG that users need to filter as they wish. The filtering is done via a text field, so I am doing a simple ObservableCollection filter with LINQ:

    async void SearchChanged(System.Object sender, Xamarin.Forms.TextChangedEventArgs e)
    {
        SearchableImages.Clear(); // ObservableCollection

        if (Search.Text.Length < 3) return;

        // ImageCollection is an always in-memory List<ImageItem>
        var filter = await Task.FromResult<IEnumerable<ImageItem>>(
            ImageCollection.Where((i) => i.Name.Contains(Search.Text.ToLower()))
            );
        foreach (var p in filter)
            SearchableImages.Add(p);
    }

This code runs extremely slowly, so much that you can see the if block there, limiting the length of the search field to 3. Enabling to search characters of any length renders this unusable.

Apparently the Filter property of a CollectionView is not available:

    <CollectionView x:Name="CollectionList" ItemsSource="{Binding SearchableIcons}" ...>

    string src = Search.Text.ToLower();
    CollectionList.Filter = (item) => item.Name.Contains("hello");

Error CS1061: 'CollectionView' does not contain a definition for 'Filter' and no accessible extension method 'Filter' accepting a first argument of type 'CollectionView' could be found (are you missing a using directive or an assembly reference?) (CS1061)

How can I filter a very large number of images smoothly?

senseiwa
  • 2,369
  • 3
  • 24
  • 47
  • 2
    I really cannot imagine filtering a list of 2000 items talkes a noticable time. Is it really the filter that is so slow? Or maybe the UI displaying the images? – Klamsi Jun 08 '21 at 09:42
  • 2
    I'm not very familiar with Xamarin, but the ObservableCollection fires a chenged event on every add. Maybe it is faster to say `SearchableImages = new(filter)` ? – Klamsi Jun 08 '21 at 10:25
  • 2
    in addition to the excellent points the other commenters made, you are running this code EVERY time a user types a character – Jason Jun 08 '21 at 11:46
  • Thanks for all the comments, it was immensely helpful! I was puzzled because Filter is not available, apparently, the question has been updated. – senseiwa Jun 11 '21 at 13:27

1 Answers1

1

The performance problem is probably not related to the actual filtering, 2000 items are not a lot and should probably be within a millisecond or so. However, await Task.FromResult seem really odd, this will not do anything asynchronous at all, and should probably just be removed altogether, synchronous filtering should be enough unless you have one or more magnitude more items. Also, you are calling Search.Text.ToLower() repeatedly without reason.

I would suspect repeated calls to SearchableImages.Add(p) that trigger UI updates etc to be the main issue. As always with performance, measure and/or profile to be sure.

To solve this I would suggest taking a look at CollectionView that has an explicit Filter property you could use. See also: Filter an observable collection

JonasH
  • 28,608
  • 2
  • 10
  • 23
  • Thanks for your answer, I've resolved by creating a new observable collection from the list, as suggested. Today I've learned that each add fires an event, and quite surprisingly, lambdas do not optimize in this case the `.ToLower()` that I thought it would be considered as constant. However, a `Filter` is not available as far as I see (question updated). – senseiwa Jun 11 '21 at 13:30