Ramda 实战案例若干

免责申明

如果你所在团队都不熟悉函数式写法,贸然在项目中写我即将展示的代码,可能会被骂。但如果你团队都是老司机,或者你自己感兴趣,学下无妨。

继续阅读之前推荐先看我之前发布的文章优雅代码指北 -- 巧用 Ramda,这篇文章介绍了 Ramda 的设计理念,解释了 point free 代码风格。了解了 point free 和函数组合的优势,再来看现在这篇文章,会好理解很多。Ramda 的 API 太多了,本文也用了很多,一个个解释是一项不可能的任务,建议去官网查用法。

你可以在 Ramda 官网的 REPL 编辑器里运行本文的代码。直接复制粘贴就行了,不用 import R from "ramda".

首先来个简单的任务热下身。

任务一:

把对象中值为空的键移除掉。只用判断一层。

答案:

import R from "ramda"; // 下面就省略导入这一步了

const clearObj = R.compose(
  R.fromPairs,
  R.reject(
    R.compose(
      R.isEmpty,
      R.last
    )
  ),
  R.toPairs
);

const obj3 = { a: {}, b: "x", c: [], d: 9, h: "", x: 0 };

clearObj(obj3);
// => {b: "x", d: 9, x: 0}
复制代码

注意,R.isEmpty 只判断数组对象和字符串,若想判断其它数据类型,可以自己写判断函数。

下面做一些复杂的数据操作。

任务二:

有这样一个状态库:

const state = {
  groupedProducts: {
    fruits: [
      { id: 11, name: "apples", price: 3 },
      { id: 12, name: "oranges", price: 4 },
      { id: 17, name: "pearls", price: 5 }
    ],
    shoes: [
      { id: 19, name: "Adidas", price: 11 },
      { id: 21, name: "Nike", price: 13 },
      { id: 18, name: "Timberland", price: 10 },
      { id: 25, name: "New Balance", price: 14 }
    ],
    vegetables: [
      { id: 31, name: "broccoli", price: 3 },
      { id: 32, name: "cabbage", price: 8 },
      { id: 33, name: "carrots", price: 4 },
      { id: 34, name: "cucumbers", price: 2 }
    ]
  },
  selectedId: 32,
  selectedTag: "vegetables",
  itemsFaved: {}
};
复制代码

要求根据选中的 id(selectedId) ,找出当前选中项,并拼接 name 和 price 属性,用于展示到浏览器 header 上。

答案:

const padStart = str => ` -- ${str}`;

const findProducts = R.converge(R.call, [
  R.compose(
    R.find,
    R.propEq("id"),
    R.prop("selectedId")
  ),
  R.converge(R.prop, [R.prop("selectedTag"), R.prop("groupedProducts")])
]);

const getHeaderTag = R.compose(
  R.converge(R.concat, [
    R.compose(
      R.toUpper,
      R.propOr("", "name")
    ),
    R.compose(
      padStart,
      R.toString,
      R.propOr("", "price")
    )
  ]),
  findProducts
);

const headerTag = getHeaderTag(state);
// => CABBAGE -- 8
复制代码

任务三:

给定用户收藏列表如下:

const favList = {
  fruits: [11],
  shoes: [19, 21],
  vegetables: [33, 34]
};
复制代码

该列表在每个商品类目下记录了用户收藏商品的 id,存在数组里。 要求在原来 groupedProducts 数据里,在已收藏的商品条目里加上 faved : true 数据。比如 Apple 的 id: 11,在收藏列表里面,修改后应为 { id: 11, name: "apples", price: 3, faved: true }

答案:

const applyFav = list =>
  R.ifElse(
    R.compose(
      R.flip(R.contains)(list),
      R.prop("id")
    ),
    R.assoc("faved", true),
    R.identity
  );

const applyFavListToProducts = R.mergeWith(
  (list, target) => R.map(applyFav(list), target),
  favList
);

const addFavToProducts = R.evolve({ groupedProducts: applyFavListToProducts });

addFavToProducts(state);
// 结果太长就不写了,可以自己试
复制代码

任务四:

根据上面提供的收藏列表,筛选原数据 groupedProducts,只保留已收藏的商品。

答案:

const getFavedItems = R.mergeWith(
  R.innerJoin((target, id) => target.id === id),
  R.__,
  favList
);

const getOnlyFavedItems = R.evolve({ groupedProducts: getFavedItems });

getOnlyFavedItems(state);
// 结果太长就不写了,可以自己试
复制代码

任务五:

开发中经常会遇到后端给的数据和 UI 需求不一致的情况,这个时候需要前端对数据进行格式化处理。 给定以下产品列表:

const products = [
  {
    productId: 31,
    productName: "cars",
    salesData: [
      { brand: "lada", rate: 0.32 },
      { brand: "mini", rate: 0.53 },
      { brand: "buick", rate: 0.22 }
    ]
  },
  {
    productId: 32,
    productName: "pc",
    salesData: [
      { brand: "lenovo", rate: 0.24 },
      { brand: "dell", rate: 0.63 },
      { brand: "hp", rate: 0.19 }
    ]
  },
  {
    productId: 34,
    productName: "mobile",
    salesData: [
      { brand: "iphone", rate: 0.78 },
      { brand: "sumsung", rate: 0.62 },
      { brand: "xiaomi", rate: 0.32 }
    ]
  }
];
复制代码

产品数据包含了产品 id,产品名称和销售数据。销售数据里包含了品牌和价格增长幅度。要求把 productId 字段塞到每个对应销售记录里面,然后把价格涨幅格式化为两个小数点的百分数。最后把销售数据按产品名称分类。

格式化后的数据应该是这样:

{
    cars:[
         { brand: "lada", rate: "32.00%", productId: 31 },
         // ...
    ],
    pc:[
        { brand: "lenovo", rate: "24.00%", productId: 32 },
        // ...
    ],
    mobile:[
        { brand: "iphone", rate: "78.00%", productId: 34 },
        // ...
    ]
}
复制代码

答案:

const toPercentage = num => (num * 100).toFixed(2) + "%";

const getSalesAndFormatRate = R.converge(
  (salesData, id) => R.map(R.merge(id), salesData),
  [
    R.compose(
      R.map(R.evolve({ rate: toPercentage })),
      R.prop("salesData")
    ),
    R.pick(["productId"])
  ]
);

const normalizeProductData = R.converge(R.zipObj, [
  R.map(R.prop("productName")),
  R.map(getSalesAndFormatRate)
]);

normalizeProductData(products);
复制代码

猜你喜欢

转载自juejin.im/post/5bb0a86e5188255c7566dcd2
今日推荐