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