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

时间:2018-01-19 18:39:16

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

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

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) 

1 个答案:

答案 0 :(得分:1)

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

&#13;
&#13;
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)
&#13;
.as-console-wrapper { max-height: 100% !important; top: 0; }
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.5.6/Rx.js"></script>
&#13;
&#13;
&#13;

使用firestore .valueChanges()

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

&#13;
&#13;
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)
&#13;
.as-console-wrapper { max-height: 100% !important; top: 0; }
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.5.6/Rx.js"></script>
&#13;
&#13;
&#13;

使用非Rx采样

在上面的代码中突出的一点是我们将数组的可观察对象转换为数组,然后返回到数组的内部可观察对象(所有这些都是为了得到一个完整的&#39;事件)。

因此,直接处理阵列更为实际 (已经使用firestore .valueChanges()进行了测试)

&#13;
&#13;
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)
&#13;
.as-console-wrapper { max-height: 100% !important; top: 0; }
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.5.6/Rx.js"></script>
&#13;
&#13;
&#13;