#angular #firebase #ionic-framework #google-cloud-firestore
#angular #firebase #ionic-framework #google-облако-firestore
Вопрос:
Каков наилучший способ создать список уникальных значений из массивов Firestore?
Каждый документ в коллекции может иметь любое количество категорий, которые записаны в массиве документов «категории».
Я пытаюсь получить все уникальные категории значений массива для заполнения параметров фильтрации, поэтому мне нужно быть уверенным, что для этой категории есть отель. Я не могу использовать только список всех категорий из другой коллекции.
Angular 8, Firestore, angularfire 2, Ionic 4
Observable:
this.hotels = afs.collection('hotels', ref => ref.where('city_id', '==', cityId).orderBy('reviews_number', 'desc')).valueChanges();
Компонентный html:
<li *ngFor="let hotel of hotels | async">
{{ hotel.categories }}
</li>
Результат:
wifi
wifi
wifi,pool,center
pool
И я не могу понять, как лучше всего получать уникальные значения из массивов, чтобы иметь возможность заполнять выпадающий список всеми вариантами уникальных значений.
wifi,pool,center
Ответ №1:
Чтобы все было чище, я бы предложил использовать отдельную переменную для размещения ваших выпадающих значений, подписаться на значения Firestore и затем извлекать уникальные значения
TYPESCRIPT:
class MyComponent {
dropDownOptions: Array<any> = [];
someMethod() { // only called once in the component
this.hotels = afs.collection('hotels', ref => ref.where('city_id', '==', cityId)
.orderBy('reviews_number', 'desc'))
.valueChanges();
this.hotels.subscribe((hotels) => {
const options: Array<string> = hotels.reduce((prevValue, hotel) => {
return [...prevValue, ...hotel.categories];
}, []);
this.dropDownOptions = options.filter(this.onlyUnique); // get unique values
})
}
onlyUnique(value, index, self) {
return self.indexOf(value) === index;
}
}
HTML:
<select>
<option *ngFor="let option of dropDownOptions" value="{{option}}">
{{ option }}
</option>
</select>
Обновить:
В HTML была опечатка. Т.е. dropdownOptions
должно было быть dropDownOptions
. Исправлено. Кроме того, я привел пример Stackblitz, который демонстрирует поведение.
Комментарии:
1. Спасибо за помощь, окончательный код должен выглядеть следующим образом: pastebin.com/B2nv5FAh но dropdownOptions пуст
2. в HTML при выполнении {{ dropDownOptions }} я получаю уникальные значения! Wi-Fi, пул, центр — это работает, просто * ngFor не возвращает значения
3. @TedAce пожалуйста, проверьте мой ответ (раздел ОБНОВЛЕНИЯ), я добавил пример stackblitz, который демонстрирует решение. Надеюсь, это поможет.
4. Работает, как я не проверял ошибку типа :))) Может быть, вы знаете, как исключить пустые значения? Поскольку теперь также включено пустое значение (у отеля нет какой-либо категории). Еще раз спасибо
5. Вы можете изменить
return [...prevValue, ...hotel.categories];
в коде наreturn [...prevValue, ...hotel.categories.filter(category => category !== "")];
, чтобы исключить пустые строки в качестве категорий. Также, пожалуйста, поддержите ответ и комментарии, если они вам помогли 🙂 Спасибо и удачного кодирования!
Ответ №2:
Вы могли бы использовать mergeMap() от RxJS для выделения отдельных объектов hotel, distinct() для фильтрации дубликатов на основе идентификатора и архивирования его обратно в массив с
toArray()
this.hotels = afs.collection('hotels', ref => ref.where('city_id', '==', cityId)
.orderBy('reviews_number', 'desc'))
.valueChanges()
.pipe(mergeMap(hotels => hotels), distinct(hotel => hotel.id), toArray());
Я не работал с Firebase, но, если бы мне пришлось догадываться, valueChanges () — это бесконечный поток. В этом случае toArray () не будет работать, и вам нужно будет сканировать() значения и помещать в массив.
this.hotels = ...valueChanges().pipe(
mergeMap(hotels => hotels),
distinct(hotel => hotel.id),
scan((acc, val) => acc.concat(...val), []));