我正在尝试使用 rx for js 来理解函数式编程。
我有一个发出“post”对象的 Rx.Observable:
每个帖子看起来都是这样的:
{
title: "sometitle",
author: "someauthor"
text: "sometext",
date: "somedate",
tags: ['tag1', 'tag2', ..., 'tagN']
}
我想将该序列转换为发出以下信号的序列:
{
tag: 'tagname',
postCount: n
}
这是我到目前为止所拥有的:
function tags(post) {
return post
.tags
.map(function(tag) { return { 'tag': tag, 'count': 1});
}
posts
.flatMap(tags)
.groupBy(function(tagged) { return tagged.tag })
. // don't know how to continue
正如我之前所说,我的目标是创建一个序列/可观察对象,为每个标签发出 {tag: 'tagname', postCount: n }
提前致谢
编辑:
我忘了提及我正在寻找“面向节点”的答案。
这就是我到目前为止所拥有的。 它有效,但我不确定 { ..., count: 1 }
部分。
我正在寻找一个更“优雅”的解决方案。
posts
.flatMap(tags)
.map((tag) => {return {name: tag, count: 1}})
.groupBy((tagcount) => {return tagcount.name})
.flatMap((taggroup) => {return taggroup.reduce((a,x) => {return {tag: x.name, count: (a.count + x.count)}})})
最佳答案
它会是这样的:
// sequesnce of posts sequence with 10ms interval
var posts = Rx.Observable
.fromArray([
{ tags: ['tag1', 'tag2'] },
{ tags: ['tag1', 'tag3'] },
{ tags: ['tag1'] },
{ tags: ['tag1', 'tag2', 'tag3'] }
])
.zip(Rx.Observable.interval(10), Rx.helpers.identity)
.do(logger('post:'));
// sequence of post counts by tags, and count changes
var tagsCountChanges = posts.scan(
function (acc, post) {
var counts = acc.counts;
var changes = [];
post.tags.forEach(function (tag) {
counts[tag] = (counts[tag] || 0) + 1;
changes.push({ tag: tag, postsCount: counts[tag] });
});
return { counts, changes };
}, { counts: {}, changes: [] })
.map(acc => acc.changes)
.do(logger('tagsCountChanges:'));
var tagCountUpdates = tagsCountChanges
.concatMap(function (changes) {
return Rx.Observable
.fromArray(changes);
});
tagCountUpdates
.forEach(logger('tagPostCounts:'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/4.1.0/rx.all.js"></script>
<pre id="log"></pre>
<script>
var log = document.getElementById('log');
function logger(label) {
return function(item) {
log.appendChild(document.createTextNode(label + ' ' + JSON.stringify(item, null, 2) + '\n'));
};
}
</script>
更新(响应编辑1):
它也可以在节点中工作:)您还可以删除帖子序列的记录器和间隔 - 它只是在浏览器中运行代码片段时显示具有中间可观察值的项目的良好日志。
i'm not sure about the
{ ..., count: 1 }
part. i'm looking for a more "elegant" solution.
实际上你可以完全删除 { ..., count: 1 }
部分:
posts
.flatMap(post => post.tags)
.groupBy(Rx.helpers.identity)
.flatMap(taggroup$ =>
taggroup$.reduce((acc,tag) => {return {tag, count: acc.count+1}}, {count:0})
)
关于优雅:我喜欢你的解决方案 - 我认为它比我的更富有表现力,也更简单。但是,我的解决方案在标签数量较多时性能会更高(因为它不会为每个标签创建内部可观察值)。
此外,我的解决方案与您的略有不同 - 它将发出标签计数更改流,而不仅仅是最终计数(帖子流完成后)。
可以轻松修改您的解决方案以获得相同的结果 - 只需将 reduce
替换为 scan
。
反之亦然 - 如果只需要总数,我的解决方案可以简化很多:
posts.reduce(
(counts, post) => {
post.tags.forEach(tag => {
counts[tag] = (counts[tag] || 0) + 1;
});
return counts;
}, {})
.flatMap(counts =>
Object.keys(counts).map(
tag => ({tag, count: counts[tag]})
)
)
关于javascript - 帖子的 rx js 聚合,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36783177/