如何使用 angularfire2 valuechanges 方法获取随机数据

Posted

技术标签:

【中文标题】如何使用 angularfire2 valuechanges 方法获取随机数据【英文标题】:How to get random data with angularfire2 valuechanges method 【发布时间】:2018-06-29 01:50:40 【问题描述】:

我有questions$ observable,它从Firestore 返回超过10 问题对象。

this.questions$ = this.moduleCollection$.doc(module.id)
  .collection('questions').valueChanges();

现在我想用10随机问题限制结果。

我可以像这样限制查询

this.questions$ = this.moduleCollection$.doc(module.id)
  .collection('questions',ref => ref.limit(10)).valueChanges();

但是不知道怎么随机获取,有Rxjs运营商这样做吗?

我尝试过的(扩展@Richard Matsen 的回答)

const sampleSize = 2
const randomIndex = (array) => Math.floor(Math.random() * array.length)
const addIndexes = (array) => array.map((item, index) => 
    item['id'] = index  
    return item
  )
const removeIndexes = (array) => array.map(item => 
   delete item.id 
   return item
 )

this.questions$ = 
this.moduleCollection$.doc(module.id).collection('questions').valueChanges()
    .map(addIndexes)
    .map(r => r[randomIndex(r)])
    .repeat()
    .scan((a, c) => a.map(a => a.id).indexOf(c.id) === -1 ? a.concat(c) : a, [])
    .skipWhile(array => array.length < sampleSize)
    .take(1)
    .map(array => array.sort((a, b) => a.id - b.id))
    .map(removeIndexes) 

【问题讨论】:

查看this answer。它是用 Java 编写的,但用 javascript 转换它应该不难 【参考方案1】:

此示例演示了一种纯 rxjs 方法,用于从数组中随机抽取样本,没有重复。

const source = Rx.Observable.of([val: 'a', val: 'b', val: 'c', val: 'd', val: 'e'])

const sampleSize = 2
const randomIndex = (array) => Math.floor(Math.random() * array.length)
const addIndexes = (array) => array.map((item, index) => 
    item['id'] = index  
    return item
  )
const removeIndexes = (array) => array.map(item => 
   delete item.id 
   return item
 )

const randomSample = (source, sampleSize) =>
  source
    .map(addIndexes)
    .map(r => r[randomIndex(r)])
    .repeat()
    .scan((a, c) => a.map(a => a.id).indexOf(c.id) === -1 ? a.concat(c) : a, [])
    .skipWhile(array => array.length < sampleSize)
    .take(1)
    .map(array => array.sort((a, b) => a.id - b.id))
    .map(removeIndexes) 

randomSample(source, sampleSize).subscribe(console.log)
.as-console-wrapper  max-height: 100% !important; top: 0; 
&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.5.6/Rx.js"&gt;&lt;/script&gt;

使用 firestore .valueChanges()

firestore .valueChanges() 方法旨在每次云上的数据发生变化时推送新数组,因此这个可观察源永远不会完成。 这意味着 repeat() 永远不会触发。为了让它工作,我们需要封装采样代码。

const source = Rx.Observable.of([val: 'a', val: 'b', val: 'c', val: 'd', val: 'e'])

const sampleSize = 2
const randomIndex = (array) => Math.floor(Math.random() * array.length)
const addIndexes = (array) => array.map((item, index) => 
    item['id'] = index  
    return item
  )
const removeIndexes = (array) => array.map(item => 
   delete item.id 
   return item
 )

const randomSample = (source, sampleSize) =>
  source
    .mergeMap(array => 
      Rx.Observable.of(array)
        .map(addIndexes)
        .map(r => r[randomIndex(r)])
        .repeat()
        .scan((a, c) => a.map(a => a.id).indexOf(c.id) === -1 ? a.concat(c) : a, [])
        .skipWhile(array => array.length < sampleSize)
        .take(1)
        .map(array => array.sort((a, b) => a.id - b.id))
        .map(removeIndexes) 
     )

randomSample(source, sampleSize).subscribe(console.log)
.as-console-wrapper  max-height: 100% !important; top: 0; 
&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.5.6/Rx.js"&gt;&lt;/script&gt;

使用非 Rx 采样

在上面的代码中突出的一点是,我们将数组的可观察对象转换为数组,然后再转换回数组的内部可观察对象(所有这些都是为了获得一个“完成”事件)。

所以,直接在数组上工作更实用。 (也已使用 firestore .valueChanges() 进行了测试)

const source = Rx.Observable.of([val: 'a', val: 'b', val: 'c', val: 'd', val: 'e'])

const sampleSize = 2
const getRandomUniqueSample = (arr, sampleSize) => 
  let result = [],
      taken = ; 
  while (result.length < Math.min(sampleSize, arr.length)) 
    let x = Math.floor(Math.random() * arr.length);
    if (!(x in taken)) 
      result.push(arr[x])
      taken[x] = null;
    
  
  return result;


const randomSample = (source, sampleSize) =>
  source
    .mergeMap(array => 
      Rx.Observable.of(getRandomUniqueSample(array, sampleSize))
    )

randomSample(source, sampleSize).subscribe(console.log)
.as-console-wrapper  max-height: 100% !important; top: 0; 
&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.5.6/Rx.js"&gt;&lt;/script&gt;

【讨论】:

感谢您的回答,我试过了,但有一些错误。我更新了我对我的问题感到厌倦的内容。请帮忙 啊,是的,你有一个对象数组,所以扫描必须更复杂一些。对象上是否有 id 属性,例如questionId 现在不行,但我可以通过.snapshotChanges 轻松完成。假设我有一个字段questionId,类似于lpybtKeZpr7lPDZMQtcO 这可行,但这只是一个小的本地更改 - 确实不应该影响您访问 Firestore 的方式。无论如何,数组项都有一个索引,我会使用它,如果你觉得它更合适,你可以拨回它。 我有点担心 concat 错误,这可能是由于缺少导入。

以上是关于如何使用 angularfire2 valuechanges 方法获取随机数据的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 AngularFire2 和 Firebase 存储上传文件数组?

如何使用 angularfire2 valuechanges 方法获取随机数据

AngularFire2:实时数据库:如何获取键和值

如何解决 rxjs Typescript 错误(Ionic 3,angularfire2)

如何在 AngularFire2 中获取当前用户的访问令牌?

Angularfire2:如何从 Firebase 存储中检索文件内容