我有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)
答案 0 :(得分: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)
&#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;
使用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)
&#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;
使用非Rx采样
在上面的代码中突出的一点是我们将数组的可观察对象转换为数组,然后返回到数组的内部可观察对象(所有这些都是为了得到一个完整的&#39;事件)。
因此,直接处理阵列更为实际
(已经使用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)
&#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;