rx js聚合帖子

时间:2016-04-22 01:28:55

标签: javascript aggregation rxjs

我正在尝试用rx来理解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)}})})

1 个答案:

答案 0 :(得分:1)

这将是这样的:

// 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):

它也可以在节点中工作:)你也可以删除帖子序列的记录器和间隔 - 它只是在浏览器中运行代码片段时显示具有中间可观察量的项目的漂亮日志。

  

我不确定{ ..., count: 1 }部分。我正在寻找一种更“优雅”的解决方案。

实际上你可以完全放弃{ ..., 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]})
     )
  )