代码之家  ›  专栏  ›  技术社区  ›  J. Hesters

Ramda:如何通过Curry、filtering和mapping来最小化计算资源?

  •  1
  • J. Hesters  · 技术社区  · 7 年前

    我正在用Ramda开发一个React应用程序。我对函数式编程还是新手(大约两个月)。

    我有这样一个联系人列表:

    const contacts = [
      {
        id: 1,
        firstName: 'Sven',
        lastName: 'Hillstedt',
        city: 'Aachen',
        company: '',
        position: 'Student',
        group: 'friends',
        tendency: 'maintain'
      },
      {
        id: 2,
        firstName: 'David',
      // ...
    ];
    

    给定一个字符串,我需要过滤这个(非常长,10.000-100.000)列表。但我只需要考虑钥匙 firstName , lastName , city , company position 。有一个数组包含以下内容:

    const FIRST_NAME = 'firstName';
    const LAST_NAME = 'lastName';
    const CITY = 'city';
    const COMPANY = 'company';
    const POSITION = 'position';
    
    export const stringFields = [FIRST_NAME, LAST_NAME, CITY, COMPANY, POSITION];
    

    现在,我使用Ramda编写了以下函数 string 以及联系人列表,在联系人键上映射,选择相关联系人并将其小写,然后返回过滤后的联系人:

    import { any, filter, includes, map, pick, pipe, toLower, values } from 'ramda';
    
    const contactIncludesValue = value =>
      pipe(
        pick(stringFields),
        map(toLower),
        values,
        any(includes(value))
      );
    
    const filterContactsByValue = value => filter(contactIncludesValue(value));
    

    正如你所看到的,这段代码很混乱(甚至认为它比强制执行要漂亮得多)。我喜欢咖喱 value => 很多次,感觉不太好。我也在质疑,这段代码是否只在联系人上迭代一次,是否有效。

    如何筛选和映射(仅选择相关键)+ lowerCase )没有重复两次或更多次的联系人列表?有没有办法避免我的咖喱和写这个清洁剂?

    0 回复  |  直到 7 年前
        1
  •  3
  •   Scott Sauyet    7 年前

    这里有几件事需要回应。

    • 即使这些评论有点尖刻,@zerkms说得对。除非您知道代码实际上 性能差,尤其是当它使代码更难编写或维护时。

    • 你不吃咖喱 value => 好几次。它只是预先准备好的,每次筛选列表时,部分应用您的值一次。

    • 你只会重复你的联系人一次。但每一个里面都有一个召唤 any 在你的字段列表上。如果找到匹配项,这个函数会提前返回,因此计算调用数并不是一件小事,但很可能是 O(m * n) 哪里 m 是字段和 n 联系人的数量。

    这个版本的代码稍微精简一些。您可能会觉得它更具可读性,也可能不会:

    const contactIncludesValue = value =>
      pipe(
        props(stringFields),
        map(toLower),
        any(includes(value))
      );
    
    const filterContactsByValue = pipe(contactIncludesValue, filter);
    

    注意 props pick(...) -> values ,以及中间产物 map(toLower) 之后效果也一样。

        2
  •  2
  •   customcommander    7 年前

    你如何过滤和映射(只选择相关键+小写)一个大的联系人列表,而无需重复两次或更多次?有没有办法避免我的咖喱和写这个清洁剂?

    如果你需要过滤 一次性转换数据,我不知道如何使用 filter 单独地

    例如,这不会保持 a 并改变它:

    const list = [
      {a: 'foo'},
      {b: 'bar'}
    ];
    
    console.log(
    
      filter(pipe(map(toUpper), has('a')), list)
    
    );
    <script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.min.js"></script>
    <script>const {filter, pipe, map, toUpper, has} = R;</script>

    为此,你需要使用 reduce 或者传感器。

    这里有一个使用传感器的解决方案。在本例中:

    1. 仅适用于以下对象: A. 财产等于 1 .
    2. 在…上 b 属性,添加 10
    3. 然后选择 B

    const list = [
      {a: 1, b: 2},
      {a: 2, b: 20},
      {a: 1, b: 3},
      {a: 2, b: 30},
      {a: 1, b: 4},
      {a: 2, b: 40},
    ];
    
    console.log(
    
    into([],
      compose(
        filter(propEq('a', 1)),
        map(over(lensProp('b'), add(10))),
        map(pick(['b']))
      ),
      list)
      
    );
    <script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.min.js"></script>
    <script>const {into, compose, filter, propEq, map, pick, over, lensProp, add} = R;</script>

    使用传感器的好处在于,它将产生结果的逻辑(数组)与转换数据的逻辑分离。

    into([]) 告诉Ramda你正在生成一个数组,因此无论你的 compose 链,将需要附加到它。

    into('') 告诉拉姆达你正在制作一个字符串。你的 组成 chain只需要返回一个字符串。 into 将负责将其连接到最终结果:

    const list = [
      {a: 1, b: 2},
      {a: 2, b: 20},
      {a: 1, b: 3},
      {a: 2, b: 30},
      {a: 1, b: 4},
      {a: 2, b: 40},
    ];
    
    console.log(
    
    into('',
      compose(
        filter(propEq('a', 1)),
        map(over(lensProp('b'), add(10))),
        map(prop('b'))
      ),
      list)
      
    );
    <script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.min.js"></script>
    <script>const {into, compose, filter, propEq, map, over, lensProp, add, prop} = R;</script>
        3
  •  1
  •   Hitmands    7 年前

    R.innerJoin 这肯定是最简洁的写作方式,但我不确定它的时间复杂性。

    const filter = value => R.innerJoin(
      // you may lowercase, etc... here
      (record, prop) => R.propEq(prop, value, record),
      R.__,
      ['firstName', 'lastName', 'city', 'company', 'position'],
    );
    
    const onlySven = filter('Sven');
    const onlyGiuseppe = filter('Giuseppe');
    
    const data = [
      {
        id: 1,
        firstName: 'Sven',
        lastName: 'Hillstedt',
        city: 'Aachen',
        company: '',
        position: 'Student',
        group: 'friends',
        tendency: 'maintain'
      },
      // ...
    ];
    
    console.log('Giuseppe', onlyGiuseppe(data));
    console.log('Sven', onlySven(data));
    <script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.min.js"></script>
    推荐文章