将此代码翻译成函数 javascript(在地图中聚合 2 个数组)

Translate this code in functional javascript (aggregate 2 array in a map)

我刚刚阅读了有关 reactive programming 的文章,我对此很感兴趣。所以我决定修改我在函数式编程方面的技能。不知道这个地方对不对

我有两个数组,一个是标签,另一个是包含标签的任务。我想把这两者合起来,带着一个tasksByTagName出去。我试过使用 lodash 但我没能以一种可读的方式做到这一点,所以我发布了我用普通 for 语句所做的事情。

此外,我有兴趣了解如何以基于流的方式思考,例如将我的聚合函数视为两个流之间的转换,正如上面链接的反应式编程文章所建议的那样。

所以,让我们从数据开始:

var tags = [ 
  { id: 1, name: 'Week 26' },
  { id: 2, name: 'week 27' },
  { id: 3, name: 'Week 25' },
  { id: 4, name: 'Week 25' }
];

var tasks = [
  {
    "name": "bar",
    "completed": false,
    "tags": [
      { "id": 1 },
      { "id": 2 }
    ]
  },
  {
    "name": "foo",
    "completed": true,
    "tags": [
      { "id": 1 }
    ]
  },
  {
    "name": "dudee",
    "completed": true,
    "tags": [
      { "id": 3 },
      { "id": 1 },
      { "id": 4 }
    ]
  }
];

这是我的一段代码:

var _ = require('lodash');

function aggregate1(tasks, tags) {

  var tasksByTags = {};
  for(var t in tasks) {
    var task = tasks[t];
    for(var i=0; i<task.tags.length; ++i) {
      var tag = task.tags[i];
      if( !tasksByTags.hasOwnProperty(tag.id) ) {
        tasksByTags[tag.id] = [];  
      }
      tasksByTags[tag.id].push(task.name);
    }
  }

  var tagById = {};
  for(var t in tags) {
    var tag = tags[t];
    tagById[tag.id] = tag.name;
  }

  var tasksByTagsName = _.mapKeys(tasksByTags, function(v, k) {
    return tagById[k];
  })

  return tasksByTagsName;
}

module.exports.aggregate1 = aggregate1;

为了完整起见,这也是带有测试数据的测试:

var tags = [ 
  { id: 1, name: 'Week 26' },
  { id: 2, name: 'week 27' },
  { id: 3, name: 'Week 25' },
  { id: 4, name: 'Week 25' }
];

var tasks = [
  {
    "name": "bar",
    "completed": false,
    "tags": [
      { "id": 1 },
      { "id": 2 }
    ]
  },
  {
    "name": "foo",
    "completed": true,
    "tags": [
      { "id": 1 }
    ]
  },
  {
    "name": "dudee",
    "completed": true,
    "tags": [
      { "id": 3 },
      { "id": 1 },
      { "id": 4 }
    ]
  }
];

var goodResults1 = {
  'Week 26': [ 'bar', 'foo', 'dudee' ],
  'week 27': [ 'bar' ],
  'Week 25': [ 'dudee' ] 
};    



var assert = require('assert')

var aggregate1 = require('../aggregate').aggregate1;

describe('Aggegate', function(){
  describe('aggregate1', function(){
    it('should work as expected', function(){
      var result = aggregate1(tasks, tags);
      assert.deepEqual(goodResults1, result);
    })
  })
})

这是一种方法:https://jsfiddle.net/ok6wf7eb/

function aggregate(tasks, tags) {

    // make an id: tag map
    var tagMap = tags.reduce(function(map, tag) {
        map[tag.id] = tag.name;
        return map;
    }, {});

    // collect tasks by tag
    return tasks.reduce(function(map, task) {
        // loop through task tags
        task.tags.forEach(function(tag) {
            // get tag name by id
            var tagId = tagMap[tag.id];
            // get array, making it on the fly if needed
            var tasks = map[tagId] || (map[tagId] = []);
            // add the new task name
            tasks.push(task.name);
        });
        return map;
    }, {});

}

这使用 reduce 作为稍微 "functional" 解决问题的方法,但为了易读性,我不确定是否比

有更多收获
var collector = {};
items.forEach(function(item) {
    // add to collector
});

下面的这种方法转化为标签对象的对象缩减。在惰性评估的帮助下,下面的过程只运行 tags.length x tasks.length 次。如果您不熟悉惰性求值,这里有一个 reference.

Example Here

script.js

function agregateTasks(tasks, tags) {

  return _.reduce(tags, function(result, tag) {

    // chain tasks to lazy evaluate
    result[tag.name] = _(tasks) 
      // get all tasks with tags that contains tag.id
      .filter({ tags: [_.pick(tag, 'id')]}) 
      // get all names
      .pluck('name') 
      // concatenate existing task names if there are
      .concat(result[tag.name] || []) 
      // only unique task name values
      .uniq() 
      // run lazy evaluation
      .value(); 

    return result;

  }, {});

}

script.spec.js

describe('aggregate()', function(){

  var tags, tasks, expectedResults;

  beforeEach(function() {
    tags = [ 
      { id: 1, name: 'Week 26' },
      { id: 2, name: 'week 27' },
      { id: 3, name: 'Week 25' },
      { id: 4, name: 'Week 25' }
    ];

    tasks = [
      {
        "name": "bar",
        "completed": false,
        "tags": [
          { "id": 1 },
          { "id": 2 }
        ]
      },
      {
        "name": "foo",
        "completed": true,
        "tags": [
          { "id": 1 }
        ]
      },
      {
        "name": "dudee",
        "completed": true,
        "tags": [
          { "id": 3 },
          { "id": 1 },
          { "id": 4 }
        ]
      }
    ];

    expectedResults = {
      'Week 26': [ 'bar', 'foo', 'dudee' ],
      'week 27': [ 'bar' ],
      'Week 25': [ 'dudee' ] 
    };

  });

  it('should work as expected', function() {

    var result = aggregateTasks(tasks, tags);
    expect(result).toEqual(expectedResults);

  });

});