Repository: Automattic/pocket-casts-android
File: modules/features/search/src/main/java/au/com/shiftyjelly/pocketcasts/search/SearchHandler.kt (Kotlin)

Code before:
                    .map { podcast ->
                        podcast.isSubscribed = true
                        FolderItem.Podcast(podcast)
                    }
                    .toList()

                podcastSearch
                    .zipWith(folderSearch) { podcasts, folders ->
                        (podcasts + folders).sortedBy { PodcastsSortType.cleanStringForSort(it.title) }
                    }
                    .toObservable()
            }
        }

    private val subscribedPodcastUuids = podcastManager
        .findSubscribedRxSingle()
        .subscribeOn(Schedulers.io())
        .toObservable()
        .map { podcasts -> podcasts.map(Podcast::uuid).toHashSet() }

    private val autoCompleteResults = searchQuery
        .subscribeOn(Schedulers.io())
        .map { it.string.trim() }
        .debounce {
            Observable.timer(200L, TimeUnit.MILLISECONDS)
        }
        .switchMap { query ->
            if (query.isEmpty()) {
                Observable.just<AutoCompleteSearch>(AutoCompleteSearch.Success(searchTerm = query, results = emptyList()))
            } else {
                rxObservable<List<SearchAutoCompleteItem>> {
                    autoCompleteManager.autoCompleteSearch(term = query)
                }.subscribeOn(Schedulers.io())
                    .map<AutoCompleteSearch> {
                        AutoCompleteSearch.Success(
                            searchTerm = query,
                            results = it
                        )
                    }
                    .onErrorReturn { exception ->
                        AutoCompleteSearch.Error(
                            searchTerm = query,
                            error = exception,
                        )
                    }
            }
        }

    private val serverSearchResults = searchQuery
        .subscribeOn(Schedulers.io())
        .map { it.copy(string = it.string.trim()) }

Review comment (suggestion):
ℹ️ I think this can be expressed as `.debounce(200L, TimeUnit.MILLISECONDS)`. The overload that uses `Observable` is useful when there is a need for different debounce values based on the input. Something like:

```kotlin
debounce { query ->
    if (query.isEmpty()) {
        Observable.just(1L)
    } else {
        Observable.timer(200L, TimeUnit.MILLISECONDS)
    }
}
```

Code after:
                    .map { podcast ->
                        podcast.isSubscribed = true
                        FolderItem.Podcast(podcast)
                    }
                    .toList()

                podcastSearch
                    .zipWith(folderSearch) { podcasts, folders ->
                        (podcasts + folders).sortedBy { PodcastsSortType.cleanStringForSort(it.title) }
                    }
                    .toObservable()
            }
        }

    private val subscribedPodcastUuids = podcastManager
        .findSubscribedRxSingle()
        .subscribeOn(Schedulers.io())
        .toObservable()
        .map { podcasts -> podcasts.map(Podcast::uuid).toHashSet() }

    private val autoCompleteResults = searchQuery
        .subscribeOn(Schedulers.io())
        .map { it.string.trim() }
        .debounce(200L, TimeUnit.MILLISECONDS)
        .switchMap { query ->
            if (query.isEmpty()) {
                Observable.just<AutoCompleteSearch>(AutoCompleteSearch.Success(searchTerm = query, results = emptyList()))
            } else {
                rxObservable<List<SearchAutoCompleteItem>> {
                    autoCompleteManager.autoCompleteSearch(term = query)
                }.subscribeOn(Schedulers.io())
                    .map<AutoCompleteSearch> {
                        AutoCompleteSearch.Success(
                            searchTerm = query,
                            results = it,
                        )
                    }
                    .onErrorReturn { exception ->
                        AutoCompleteSearch.Error(
                            searchTerm = query,
                            error = exception,
                        )
                    }
            }
        }

    private val serverSearchResults = searchQuery
        .subscribeOn(Schedulers.io())
        .map { it.copy(string = it.string.trim()) }
        .debounce {
            val debounceQuery = it.string.isNotEmpty() && !it.immediate