在Underscore / Ramda / Functional语言/库中组合多个filter()谓词

时间:2015-10-08 11:25:24

标签: javascript dictionary filter functional-programming underscore.js

对于我想要使用多个谓词过滤它的数据列表,然后对每个过滤器执行操作。

EG。如果我的数据是:

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 s的数组。因此,第二个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;
                })
)

如果我错了,请纠正我但是,这需要一个多次方法解决我的效率低下的问题随着数组列表的增长和谓词数量的增长,意见越来越强烈。

使用命令式方法很容易写出来。包含多个for语句的单个if循环。高效但不优雅:)

请使用功能javascript(如Underscore,LoDash或Excellent RamdaJS库)建议最佳方法。它是如何用纯函数式语言完成的?

注意:

  1. 在这种情况下,数组顺序并不重要。
  2. 请不要逐字逐句地提出示例并建议其他解决方案,我想要一个通用解决方案来过滤和映射多个谓词列表。

4 个答案:

答案 0 :(得分:1)

尝试这样:



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 , ' ' ) );
&#13;
.line {
  border : solid 1px #AAA;
  padding : 3px;
  margin : 3px;
  }
&#13;
<div id='dbg'></div>
&#13;
&#13;
&#13;

答案 1 :(得分:1)

使用Array.prototype.map迭代对象,克隆对象(使用Object.assign()_.assign()_.extend())以防止更改原始对象,并使用{{应用过滤器3}}:

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>

答案 2 :(得分:1)

并行完成2个过滤操作的要求,指的是在这里使用reduce,而不是filter。

使用lodash-fp专门使用流功能样式,否则可以使用普通的lodash / underscore。

&#13;
&#13;
   

 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))
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash-fp/0.10.4/lodash-fp.js"></script>
&#13;
&#13;
&#13;

答案 3 :(得分:0)

这是我的解决方案。告诉我whatcha的想法!

我认为这种解决方案很优雅,而且我认为它也是线性时间。 您可能不需要像我一样使用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>