在下划线/ Ramda /函数式语言/库中组合多个filter()谓词



对于一个数据列表,我想使用几个谓词对其进行过滤,然后对每个过滤器执行操作。

。如果我的数据是:

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库。在纯函数式语言中是如何实现的?

注意:

  1. 数组顺序在这种情况下并不重要。
  2. 请不要从字面上理解这个例子并建议替代解决方案,我想要一个通用的解决方案来过滤和映射多个谓词的列表。

试试:

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'));

最新更新