在功能性javascript中翻译此代码(地图中的聚合2数组)

时间:2015-07-01 21:34:02

标签: javascript functional-programming reactive-programming lodash

我刚读过关于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);
    })
  })
})

2 个答案:

答案 0 :(得分:2)

下面的方法转换为标签对象的对象缩减。借助延迟评估,以下流程仅运行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);

  });

});

答案 1 :(得分:1)

以下是一种方法: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作为问题的一种稍微“功能性”的方法,但为了清晰起见我永远不能确定它有多大的收获

var collector = {};
items.forEach(function(item) {
    // add to collector
});