对于一个数据列表,我想使用几个谓词对其进行过滤,然后对每个过滤器执行操作。
。如果我的数据是:
var people = [
{name: 'Sachin', profession: 'doctor', cases: 12},
{name: 'Djokovic', profession: 'lawyer', cases: 14},
{name: 'Paes', profession: 'doctor', cases: 36},
{name: 'Jordan', profession: 'lawyer', cases: 78},
{name: 'Williams', profession: 'doctor', cases: 30},
{name: 'Nehwal', profession: 'lawyer', cases: 75}
]
我想把它转换成:
var peopleWithoutCases = [
{name: 'Sachin', profession: 'doctor', patients: 12, cases: 12},
{name: 'Djokovic', profession: 'lawyer', courtcases: 14, cases: 14},
{name: 'Paes', profession: 'doctor', patients: 36, cases: 36},
{name: 'Jordan', profession: 'lawyer', courtcases: 78, cases: 78},
{name: 'Williams', profession: 'doctor', patients: 30, cases: 30},
{name: 'Nehwal', profession: 'lawyer', courtcases: 75, cases: 75}
]
有这样一个优雅的函数方法吗??
people
.filter (person => person.profession == 'doctor')
.map (person => {
person.patients = person.cases
return person;
})
.filter (person => person.profession == 'lawyer')
.map (person => {
person.courtcases = person.cases
return person;
})
问题是第一个map
返回一个只有doctor
的数组。因此第二个filter
返回[]
。
我知道我可以这样做:
_.union(
people
.filter (person => person.profession == 'doctor')
.map (person => {
person.patients = person.cases
return person;
}),
people
.filter (person => person.profession == 'lawyer')
.map (person => {
person.courtcases = person.cases
return person;
})
)
请纠正我,如果我错了但是,这需要一个多通道方法来解决问题,这是低效在我看来,随着数组列表的增长和谓词数量的增长。
很容易用命令式方法来写。一个包含多个if
语句的for
循环。高效但不优雅:)
请建议使用函数式javascript的最佳方法,如下划线,LoDash或优秀的RamdaJS库。在纯函数式语言中是如何实现的?
注意:
- 数组顺序在这种情况下并不重要。
- 请不要从字面上理解这个例子并建议替代解决方案,我想要一个通用的解决方案来过滤和映射多个谓词的列表。
试试:
var el = document.getElementById('dbg');
var $l = function( val ){
el.innerHTML = el.innerHTML + '<div class="line"><pre>' + val + '</pre></div>';
}
var people = [
{name: 'Sachin', profession: 'doctor', cases: 12},
{name: 'Djokovic', profession: 'lawyer', cases: 14},
{name: 'Paes', profession: 'doctor', cases: 36},
{name: 'Jordan', profession: 'lawyer', cases: 78},
{name: 'Williams', profession: 'doctor', cases: 30},
{name: 'Nehwal', profession: 'lawyer', cases: 75},
{name: 'Nawak', profession: 'developper', cases: 750}
]
var modifiers = {
'doctor' : function( ){ this.patients =this.cases ; return this} ,
'lawyer' : function( ){ this.courtcases = this.cases; return this; } ,
'developper' : function( ){ this.program = this.cases; return this; } ,
}
var result = people.map(function( p){
modifiers[p.profession].call( p );
return p;
})
console.dir(result);
$l( JSON.stringify( result , null , ' ' ) );
.line {
border : solid 1px #AAA;
padding : 3px;
margin : 3px;
}
<div id='dbg'></div>
要求并行执行2个过滤器操作,这里指示使用reduce,而不是filter。
使用lodash-fp专门使用流函数风格,否则可以使用普通的lodash/underscore。
var people = [
{name: 'Sachin', profession: 'doctor', cases: 12},
{name: 'Djokovic', profession: 'lawyer', cases: 14},
{name: 'Paes', profession: 'doctor', cases: 36},
{name: 'Jordan', profession: 'lawyer', cases: 78},
{name: 'Williams', profession: 'doctor', cases: 30},
{name: 'Nehwal', profession: 'lawyer', cases: 75},
{name: 'Nawak', profession: 'developper', cases: 750}
]
var mapper = {"doctor": "patients", "lawyer": "courtcases"};
var process = _.flow(_.reduce((r, e, i) => [...r, mapper[e.profession] && {
...e,
[mapper[e.profession]]: e.cases
}], []), _.compact);
console.log(process(people))
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash-fp/0.10.4/lodash-fp.js"></script>
使用Array.prototype.map
来迭代对象,克隆对象(使用Object.assign()
, _.assign()
或_.extend()
)以防止更改原始对象,并使用array# reduce:
var people = [{"name":"Sachin","profession":"doctor","cases":12},{"name":"Djokovic","profession":"lawyer","cases":14},{"name":"Paes","profession":"doctor","cases":36},{"name":"Jordan","profession":"lawyer","cases":78},{"name":"Williams","profession":"doctor","cases":30},{"name":"Nehwal","profession":"lawyer","cases":75}];
var propsMap = {
'doctor': 'patients',
'lawyer': 'courtcases'
};
var filters = [
function cases2Prof(person) {
person[propsMap[person.profession]] = person.cases;
return person;
},
function removeCases(person) {
delete person.case;
return person;
}
];
var result = applyFilters(people, filters);
function applyFilters(array, filters) {
return array.map(function(item) {
var cloned = Object.assign({}, item); // clone the object to prevent mutating the original
// run all filters on the object
return filters.reduce(function(object, filter) {
return filter(cloned);
}, cloned);
});
}
console.table(result);
document.getElementById('demo').innerHTML = JSON.stringify(result, null, ' ');
<pre id="demo"></pre>
这是我的解决方案。告诉我你是怎么想的!
我认为这个解决方案很优雅,我相信它也是线性时间。你可能不需要像我一样使用Ramda.js。
var people = [
{name: 'Sachin', profession: 'doctor', cases: 12},
{name: 'Djokovic', profession: 'lawyer', cases: 14},
{name: 'Paes', profession: 'doctor', cases: 36},
{name: 'Jordan', profession: 'lawyer', cases: 78},
{name: 'Williams', profession: 'doctor', cases: 30},
{name: 'Nehwal', profession: 'lawyer', cases: 75}
]
const professionToCaseType = {
doctor: 'patients',
lawyer: 'courtcases',
}
const transformPerson = (person) =>
R.assoc(professionToCaseType[person.profession], person.cases, person)
const transformPeople =
R.map(transformPerson)
console.log(transformPeople(people))
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.min.js"></script>
使用下划线的优雅解决方案如下:
var caseType = {
doctor: 'patients',
lawyer: 'courtcases',
};
var peopleWithoutCases = _.map(people, person => _.extend({
[caseType[person.profession]]: person.cases,
}, person);
注意peopleWithoutCases
有点用词不当,因为cases
属性仍然包含在结果中。如果你真的想把它们去掉,这很容易修复:
var caseType = {
doctor: 'patients',
lawyer: 'courtcases',
};
var peopleWithoutCases = _.map(people, person => _.extend({
[caseType[person.profession]]: person.cases,
}, _.omit(person, 'cases'));