如何随机化(洗牌)一个 angular Observable?
How to randomize (shuffle) an angular Observable?
我正在使用 angular 和 firebase。我在 Firebase 中有 170 种产品。当我调用 firebase 时,我得到了存储在 products$ 中的产品的 Observable。
问题:我想将所有产品相互混洗。这样当我刷新网页的时候,每次的产品列表都会不一样。我尝试了数组方法,但它不起作用,因为 products$ 是一个 Observable 而不是数组!
我能做什么?提前致谢!
product.component.ts
import { Product } from './../models/product';
import { Cart } from './../models/cart';
import { CartService } from './../cart.service';
import { ActivatedRoute } from '@angular/router';
import { ProductsService } from './../products.service';
import { map, switchMap } from 'rxjs/operators';
import { Observable, of, Subscription } from 'rxjs';
import { Component } from '@angular/core';
@Component({
selector: 'products',
templateUrl: './products.component.html',
styleUrls: ['./products.component.css'],
})
export class ProductsComponent {
products$: Observable<Product[]>;
cart$: Observable<Cart>;
constructor(
private productsService: ProductsService,
private cartService: CartService,
private route: ActivatedRoute
) {
this.getProducts();
this.getCart();
}
private getProducts(): void {
this.products$ = this.route.queryParamMap.pipe(
switchMap((params) => {
if (!params) return of(null);
let category = params.get('category');
return this.applyFilter(category);
})
);
}
private applyFilter(category: string): Observable<Product[]> {
if (!category)
return this.productsService
.getAll()
.snapshotChanges()
.pipe(
map((sps) => sps.map((sp) => ({ key: sp.key, ...sp.payload.val() })))
);
return this.productsService
.getByCategory(category)
.snapshotChanges()
.pipe(
map((sps) => sps.map((sp) => ({ key: sp.key, ...sp.payload.val() })))
);
}
private async getCart() {
this.cart$ = (await this.cartService.getCart())
.snapshotChanges()
.pipe(
map(
(sc) =>
new Cart(
sc.key,
sc.payload.val().cartLines,
sc.payload.val().createdOn
)
)
);
}
}
product.component.html
<div class="row">
<div class="col-3">
<products-filter></products-filter>
</div>
<div class="col-9">
<div *ngIf="cart$ | async as cart" class="row">
<ng-container *ngFor="let product of products$ | async; let i = index">
<div class="col">
<product-card [product]="product" [cart]="cart"></product-card>
<div *ngIf="(i + 1) % 2 === 0" class="row row-cols-2"></div>
</div>
</ng-container>
</div>
<div *ngIf="(products$ | async)?.length === 0" class="alert alert-info" role="alert">
<h1>Keine Produkte gefunden!</h1>
</div>
</div>
</div>
肯定有 better/shorter 方法可以做到这一点,但您可以尝试使用“BehaviourSubject”并将其传递给您的数组,按您的喜好排序(在本例中为随机排序)。
您可以尝试这样的操作:
- 将此添加到您的 product.component.ts:
// product.component.ts
import { ActivatedRoute } from '@angular/router';
import { Component } from '@angular/core';
import { BehaviorSubject, Observable, of, Subscription } from 'rxjs';
import { map, switchMap } from 'rxjs/operators';
import { Product } from './../models/product';
import { Cart } from './../models/cart';
import { CartService } from './../cart.service';
import { ProductsService } from './../products.service';
@Component({
selector: 'products',
templateUrl: './products.component.html',
styleUrls: ['./products.component.css'],
})
export class ProductsComponent {
products$: Observable<Product[]>;
randomProductsSource = new BehaviorSubject<Product[] | null>(null);
randomProducts$ = this.randomProductsSource.asObservable();
cart$: Observable<Cart>;
constructor(
private productsService: ProductsService,
private cartService: CartService,
private route: ActivatedRoute
) {
this.getProducts();
this.getCart();
this.products$.subscribe( _products => {
if ( _products && products?.length > 0) {
this.randomizeArray(_products);
this.setRandomProducts(_products);
}
})
}
private getProducts(): void {
this.products$ = this.route.queryParamMap.pipe(
switchMap((params) => {
if (!params) return of(null);
let category = params.get('category');
return this.applyFilter(category);
})
);
}
private applyFilter(category: string): Observable<Product[]> {
if (!category)
return this.productsService
.getAll()
.snapshotChanges()
.pipe(
map((sps) => sps.map((sp) => ({ key: sp.key, ...sp.payload.val() })))
);
return this.productsService
.getByCategory(category)
.snapshotChanges()
.pipe(
map((sps) => sps.map((sp) => ({ key: sp.key, ...sp.payload.val() })))
);
}
private async getCart() {
this.cart$ = (await this.cartService.getCart())
.snapshotChanges()
.pipe(
map(
(sc) =>
new Cart(
sc.key,
sc.payload.val().cartLines,
sc.payload.val().createdOn
)
)
);
}
private setRandomProducts(data: Product[] | null) {
this.randomProductsSource.next(data);
}
private randomizeArray(arrayProducts) {
for (let i = arrayProducts.length-1; i > 0; i--) {
const j = Math.floor( Math.random()*(i+1) );
[arrayProducts[i], arrayProducts[j]] = [arrayProducts[j], arrayProducts[i]];
}
}
}
- 然后在你的 product.component.html 中更改,只需将 products$ 更改为 randomProducts$:
<div *ngIf="(randomProducts$ | async)?.length === 0" class="alert alert-info" role="alert">
我从 HERE
得到了随机化方法的想法
只需在映射中添加一个额外的步骤,即在返回数组之前实际打乱数组。但是你想洗牌,这是一个选择,这里有几个选项:How to randomize (shuffle) a JavaScript array? 那里的答案之一介绍了这个:
/* Randomize array in-place using Durstenfeld shuffle algorithm */
shuffleArray(array) {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
return array;
}
所以只要在你想打乱数组的地方调用这个函数就可以了。执行过滤器后,适当的时间将在 getProducts
函数内:
private getProducts(): void {
this.products$ = this.route.queryParamMap.pipe(
switchMap((params) => {
if (!params) return of(null);
let category = params.get('category');
return this.applyFilter(category);
}),
// Shuffle the array before returning it!
map((products: Product[]) => this.shuffleArray(products))
);
}
我正在使用 angular 和 firebase。我在 Firebase 中有 170 种产品。当我调用 firebase 时,我得到了存储在 products$ 中的产品的 Observable。
问题:我想将所有产品相互混洗。这样当我刷新网页的时候,每次的产品列表都会不一样。我尝试了数组方法,但它不起作用,因为 products$ 是一个 Observable 而不是数组! 我能做什么?提前致谢!
product.component.ts
import { Product } from './../models/product';
import { Cart } from './../models/cart';
import { CartService } from './../cart.service';
import { ActivatedRoute } from '@angular/router';
import { ProductsService } from './../products.service';
import { map, switchMap } from 'rxjs/operators';
import { Observable, of, Subscription } from 'rxjs';
import { Component } from '@angular/core';
@Component({
selector: 'products',
templateUrl: './products.component.html',
styleUrls: ['./products.component.css'],
})
export class ProductsComponent {
products$: Observable<Product[]>;
cart$: Observable<Cart>;
constructor(
private productsService: ProductsService,
private cartService: CartService,
private route: ActivatedRoute
) {
this.getProducts();
this.getCart();
}
private getProducts(): void {
this.products$ = this.route.queryParamMap.pipe(
switchMap((params) => {
if (!params) return of(null);
let category = params.get('category');
return this.applyFilter(category);
})
);
}
private applyFilter(category: string): Observable<Product[]> {
if (!category)
return this.productsService
.getAll()
.snapshotChanges()
.pipe(
map((sps) => sps.map((sp) => ({ key: sp.key, ...sp.payload.val() })))
);
return this.productsService
.getByCategory(category)
.snapshotChanges()
.pipe(
map((sps) => sps.map((sp) => ({ key: sp.key, ...sp.payload.val() })))
);
}
private async getCart() {
this.cart$ = (await this.cartService.getCart())
.snapshotChanges()
.pipe(
map(
(sc) =>
new Cart(
sc.key,
sc.payload.val().cartLines,
sc.payload.val().createdOn
)
)
);
}
}
product.component.html
<div class="row">
<div class="col-3">
<products-filter></products-filter>
</div>
<div class="col-9">
<div *ngIf="cart$ | async as cart" class="row">
<ng-container *ngFor="let product of products$ | async; let i = index">
<div class="col">
<product-card [product]="product" [cart]="cart"></product-card>
<div *ngIf="(i + 1) % 2 === 0" class="row row-cols-2"></div>
</div>
</ng-container>
</div>
<div *ngIf="(products$ | async)?.length === 0" class="alert alert-info" role="alert">
<h1>Keine Produkte gefunden!</h1>
</div>
</div>
</div>
肯定有 better/shorter 方法可以做到这一点,但您可以尝试使用“BehaviourSubject”并将其传递给您的数组,按您的喜好排序(在本例中为随机排序)。
您可以尝试这样的操作:
- 将此添加到您的 product.component.ts:
// product.component.ts
import { ActivatedRoute } from '@angular/router';
import { Component } from '@angular/core';
import { BehaviorSubject, Observable, of, Subscription } from 'rxjs';
import { map, switchMap } from 'rxjs/operators';
import { Product } from './../models/product';
import { Cart } from './../models/cart';
import { CartService } from './../cart.service';
import { ProductsService } from './../products.service';
@Component({
selector: 'products',
templateUrl: './products.component.html',
styleUrls: ['./products.component.css'],
})
export class ProductsComponent {
products$: Observable<Product[]>;
randomProductsSource = new BehaviorSubject<Product[] | null>(null);
randomProducts$ = this.randomProductsSource.asObservable();
cart$: Observable<Cart>;
constructor(
private productsService: ProductsService,
private cartService: CartService,
private route: ActivatedRoute
) {
this.getProducts();
this.getCart();
this.products$.subscribe( _products => {
if ( _products && products?.length > 0) {
this.randomizeArray(_products);
this.setRandomProducts(_products);
}
})
}
private getProducts(): void {
this.products$ = this.route.queryParamMap.pipe(
switchMap((params) => {
if (!params) return of(null);
let category = params.get('category');
return this.applyFilter(category);
})
);
}
private applyFilter(category: string): Observable<Product[]> {
if (!category)
return this.productsService
.getAll()
.snapshotChanges()
.pipe(
map((sps) => sps.map((sp) => ({ key: sp.key, ...sp.payload.val() })))
);
return this.productsService
.getByCategory(category)
.snapshotChanges()
.pipe(
map((sps) => sps.map((sp) => ({ key: sp.key, ...sp.payload.val() })))
);
}
private async getCart() {
this.cart$ = (await this.cartService.getCart())
.snapshotChanges()
.pipe(
map(
(sc) =>
new Cart(
sc.key,
sc.payload.val().cartLines,
sc.payload.val().createdOn
)
)
);
}
private setRandomProducts(data: Product[] | null) {
this.randomProductsSource.next(data);
}
private randomizeArray(arrayProducts) {
for (let i = arrayProducts.length-1; i > 0; i--) {
const j = Math.floor( Math.random()*(i+1) );
[arrayProducts[i], arrayProducts[j]] = [arrayProducts[j], arrayProducts[i]];
}
}
}
- 然后在你的 product.component.html 中更改,只需将 products$ 更改为 randomProducts$:
<div *ngIf="(randomProducts$ | async)?.length === 0" class="alert alert-info" role="alert">
我从 HERE
得到了随机化方法的想法只需在映射中添加一个额外的步骤,即在返回数组之前实际打乱数组。但是你想洗牌,这是一个选择,这里有几个选项:How to randomize (shuffle) a JavaScript array? 那里的答案之一介绍了这个:
/* Randomize array in-place using Durstenfeld shuffle algorithm */
shuffleArray(array) {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
return array;
}
所以只要在你想打乱数组的地方调用这个函数就可以了。执行过滤器后,适当的时间将在 getProducts
函数内:
private getProducts(): void {
this.products$ = this.route.queryParamMap.pipe(
switchMap((params) => {
if (!params) return of(null);
let category = params.get('category');
return this.applyFilter(category);
}),
// Shuffle the array before returning it!
map((products: Product[]) => this.shuffleArray(products))
);
}