Total Cart Price: Exercise

Write a point-free function to calculate a shopping cart’s total price in dollars.

Calculate a shopping cart’s total price in dollars.
Usage

  1. const price = getTotalPrice(cart); // '$44.20'

cart.js

  1. export default [{
  2. name: 'apples',
  3. price: 2.49
  4. }, {
  5. name: 'soap',
  6. price: 1.99
  7. }, {
  8. name: 'milk',
  9. price: 2.99
  10. }, {
  11. name: 'eggs',
  12. price: 3.99
  13. }, {
  14. name: 'carrots',
  15. price: 2.99
  16. }, {
  17. name: 'butter',
  18. price: 1.49
  19. }, {
  20. name: 'fish',
  21. price: 9
  22. }, {
  23. name: 'lettuce',
  24. price: 2.99
  25. }, {
  26. name: 'broccoli',
  27. price: 4.99
  28. }, {
  29. name: 'lemons',
  30. price: 3.49
  31. }];

index.js

用了pluck不用prop!!!!

  1. import {pluck,prop,sum,compose,concat} from 'ramda';
  2. import cart from './cart';
  3. const getTotalPrice = compose(
  4. concat("$"),//不可以 只能接受list
  5. toString,
  6. sum,
  7. pluck("price")
  8. );

Total Cart Price: Solution Review

Solution review.
We’ll cover the following

Getting Prices #

Since point-free takes some getting used to, it can help to first write a plain solution and make it point-free later.
If you need a cart’s total price and each item’s shaped like this…

  1. {
  2. name: 'apples',
  3. price: 2.49
  4. }

…start by planning how you’d initially prepare the data. It’s a collection so map can get the prices and reduce can add them up for us.

  1. import cart from './cart';
  2. const getTotalPrice = (items) => items
  3. .map((item) => item.price)
  4. .reduce((acc, value) => acc + value, 0)
  5. const result = getTotalPrice(cart);
  6. console.log({ result });

A Touch of Formatting #

We’d like a dollar figure, so 36.410000000000004 should be $36.41.

  1. import cart from './cart';
  2. const toUSD = (amount) => amount.toLocaleString('en-US', {
  3. style: 'currency',
  4. currency: 'USD',
  5. });
  6. const getTotalPrice = (items) => {
  7. const total = items
  8. .map((item) => item.price)
  9. .reduce((acc, value) => acc + value, 0);
  10. return toUSD(total);
  11. };
  12. const result = getTotalPrice(cart);
  13. console.log({ result });

Refactoring to Point-Free #

What were our steps?

  1. Get each price
  2. Add them up
  3. Format as dollars (USD)

We know pipe’s great for multiple steps, so that’ll definitely help us get a point-free solution. And map/reduce are provided by Ramda.

  1. import { map, pipe, reduce } from 'ramda';
  2. import cart from './cart';
  3. const toUSD = (amount) => amount.toLocaleString('en-US', {
  4. style: 'currency',
  5. currency: 'USD',
  6. });
  7. const getTotalPrice = pipe(
  8. map((item) => item.price),
  9. reduce((acc, value) => acc + value, 0),
  10. toUSD
  11. );
  12. const result = getTotalPrice(cart);
  13. console.log({ result });

Even though the map/reduce functions aren’t point-free, getTotalPrice is. This solution’s perfectly fine so pat yourself on the back if you got it.
We can make map/reduce point-free though. Ramda’s prop function grabs a given property of an object.

  1. map(prop('price'))

And Ramda’s add function can be put inside reduce.

  1. reduce(add, 0);
  1. import { add, map, pipe, prop, reduce } from 'ramda';
  2. import cart from './cart';
  3. const toUSD = (amount) => amount.toLocaleString('en-US', {
  4. style: 'currency',
  5. currency: 'USD',
  6. });
  7. const getTotalPrice = pipe(
  8. map(prop('price')),
  9. reduce(add, 0),
  10. toUSD
  11. );
  12. const result = getTotalPrice(cart);
  13. console.log({ result });

Wait There’s More! #

Plucking a property and summing a list are such common use cases that Ramda provides functions for them!
Check out pluck and sum.
https://ramdajs.com/docs/#pluck
https://ramdajs.com/docs/#sum

  1. import { add, pipe, pluck, sum } from 'ramda';
  2. import cart from './cart';
  3. const toUSD = (amount) => amount.toLocaleString('en-US', {
  4. style: 'currency',
  5. currency: 'USD',
  6. });
  7. const getTotalPrice = pipe(
  8. pluck('price'),
  9. sum,
  10. toUSD
  11. );
  12. const result = getTotalPrice(cart);
  13. console.log({ result });

I personally liked this solution the most as it’s so concise and expressive. If you got this one then double-pat yourself on the back.

toLocalString

Array.prototype.toLocaleString()

toLocaleString() 返回一个字符串表示数组中的元素。数组中的元素将使用各自的 toLocaleString 方法转成字符串,这些字符串将使用一个特定语言环境的字符串(例如一个逗号 “,”)隔开。

  1. const array1 = [1, 'a', new Date('21 Dec 1997 14:12:00 UTC')];
  2. const localeString = array1.toLocaleString('en', { timeZone: 'UTC' });
  3. console.log(localeString);
  4. // expected output: "1,a,12/21/1997, 2:12:00 PM",
  5. // This assumes "en" locale and UTC timezone - your results may vary

参数

locales 可选
带有BCP 47语言标记的字符串或字符串数组,关于locales参数的形式与解释,请看Intl页面。
options 可选
一个可配置属性的对象,对于数字 Number.prototype.toLocaleString(),对于日期Date.prototype.toLocaleString().

  1. "zh-Hans-CN" 在中国使用的简体中文 (primary language with script and country codes)
  1. en-US

Number.prototype.toLocaleString()

toLocaleString() 方法返回这个数字在特定语言环境下的表示字符串。
新的 locales 和 options 参数让应用程序可以指定要进行格式转换的语言,并且定制函数的行为。在旧的实现中,会忽略 locales 和 options 参数,使用的语言环境和返回的字符串的形式完全取决于实现方式。

options
可选. 包含一些或所有的下面属性的类:

  • “decimal” 用于纯数字格式;
  • “currency” 用于货币格式;
  • “percent” 用于百分比格式;
  • “unit” 用于单位格式

localeMatcher
使用的 local 的匹配算法. 可能的值有 “lookup 和 “best fit”; 默认值是 “best fit”. 有关此选项更多的信息, 请参见 Intl page.
style
要使用的格式样式,默认为 “decimal”。

currency
在货币格式化中使用的货币符号. 可能的值是ISO的货币代码 (the ISO 4217 currency codes,) 例如 “USD” 表示美元, “EUR” 表示欧元, 或者 “CNY”是人民币 — 更多请参考 Current currency & funds code list。没有默认值,如果 style 是 “currency”,必须提 currency 属性.

转为美元

  1. const toUSD = (amount) => amount.toLocaleString('en-US', {
  2. style: 'currency',
  3. currency: 'USD',
  4. });

转为人民币

如:zh-u-nu-hanidec(表示中文十进制数字)
“zh-Hans-CN” 在中国使用的简体中文 (primary language with script and country codes)

  1. const toCNY = (amount) => amount.toLocaleString('zh-Hans-CN', {
  2. style: 'currency',
  3. currency: 'CNY',
  4. });
  5. { result: 'CN¥36.41' }