【问题标题】:How to get random data with angularfire2 valuechanges method如何使用 angularfire2 valuechanges 方法获取随机数据
【发布时间】: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 转换它应该不难

标签: firebase rxjs observable angularfire2 google-cloud-firestore


【解决方案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 错误,这可能是由于缺少导入。
猜你喜欢
  • 2018-11-03
  • 2018-04-17
  • 2018-05-27
  • 2022-01-07
  • 2018-03-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多