帖子的 rx js 聚合

rx js aggregation for posts

我正在尝试使用 rx for js 了解函数式编程。

我有一个 Rx.Observable 发射 "post" 个对象:

每个 post 看起来像这样:

{
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 

正如我之前所说,我的目标是创建一个 sequence/observable 为每个标签

发出 {tag: 'tagname', postCount: n }

提前谢谢

编辑:

我忘了说我在寻找 "node oriented" 答案。

这就是我目前所拥有的。 它有效,但我不确定 { ..., count: 1 } 部分。 我正在寻找更 "elegant" 的解决方案。

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>

更新(响应edit1):

它也可以在节点中工作:)您也可以删除日志记录器和帖子序列的间隔 - 它只是为了在浏览器中 运行 片段时显示带有中间可观察项的项目的良好日志。

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]})
     )
  )