Median Paycheck: Exercise

Write a point-free function to find the median monthly paycheck above $100,000.

I hope this one looks familiar–it’s from our higher-order functions section!
Given a list of employee salaries, find the median monthly paycheck above $100,000.
Usage

  1. const medianPaycheck = getMedianPaycheck(employees) // $141,000

The median’s $141,000?! Must be Google or Facebook employees…oh, by the way, your solution must be point-free. ;)
employees.js

  1. export default [
  2. {
  3. "name": "User 1",
  4. "salary": 55042
  5. },
  6. {
  7. "name": "User 2",
  8. "salary": 74274
  9. },
  10. {
  11. "name": "User 3",
  12. "salary": 998323
  13. },
  14. {
  15. "name": "User 4",
  16. "salary": 97863
  17. },
  18. {
  19. "name": "User 5",
  20. "salary": 116609
  21. },
  22. {
  23. "name": "User 6",
  24. "salary": 402591
  25. },
  26. {
  27. "name": "User 7",
  28. "salary": 684812
  29. },
  30. {
  31. "name": "User 8",
  32. "salary": 135921
  33. },
  34. {
  35. "name": "User 9",
  36. "salary": 736885
  37. },
  38. {
  39. "name": "User 10",
  40. "salary": 85801
  41. },
  42. {
  43. "name": "User 11",
  44. "salary": 532593
  45. },
  46. {
  47. "name": "User 12",
  48. "salary": 64776
  49. },
  50. {
  51. "name": "User 13",
  52. "salary": 71243
  53. },
  54. {
  55. "name": "User 14",
  56. "salary": 740326
  57. },
  58. {
  59. "name": "User 15",
  60. "salary": 1039627
  61. },
  62. {
  63. "name": "User 16",
  64. "salary": 243305
  65. },
  66. {
  67. "name": "User 17",
  68. "salary": 957457
  69. },
  70. {
  71. "name": "User 18",
  72. "salary": 100250
  73. },
  74. {
  75. "name": "User 19",
  76. "salary": 24994
  77. },
  78. {
  79. "name": "User 20",
  80. "salary": 71097
  81. }
  82. ];

solution

  1. import { filter, lte, pipe, pluck,median, divide,flip } from 'ramda';
  2. import employees from './employees.js';
  3. const toUSD = x=>x.toLocaleString("en-US",{
  4. style:"currency",
  5. currency:"USD"
  6. })
  7. const getMedianPaycheck = pipe(
  8. pluck("salary"),
  9. filter(lte(100000)),
  10. x=>{console.log(x);return x},
  11. median,
  12. flip(divide)(12),
  13. toUSD
  14. );
  15. const medianPaycheck = getMedianPaycheck(employees) // $141,000
  16. console.log(medianPaycheck)

Median Paycheck: Solution Review

Solution review.

I hope you didn’t cheat and look at the HOFs section to find this snippet. :D
Either way, let’s list the steps

  1. Get salaries
  2. Reject anything below $100,000
  3. Get the median
  4. Calculate monthly paycheck (amount / 12 months)
  5. Format dollars (USD)

I think we’re comfortable enough to start with a Ramda solution

  1. import { filter, map, median, pipe, prop } from 'ramda';
  2. import employees from './employees';
  3. const toUSD = (amount) => amount.toLocaleString('en-US', {
  4. style: 'currency',
  5. currency: 'USD',
  6. });
  7. const getMedianPaycheck = pipe(
  8. map(prop('salary')),
  9. filter((amount) => amount >= 100000),
  10. median,
  11. (amount) => amount / 12,
  12. toUSD
  13. );
  14. const result = getMedianPaycheck(employees);
  15. console.log({ result });

Remember, pluck(‘salary’) is equivalent to map(prop(‘salary’)).

  1. import { filter, median, pipe, pluck } from 'ramda';
  2. import employees from './employees';
  3. const toUSD = (amount) => amount.toLocaleString('en-US', {
  4. style: 'currency',
  5. currency: 'USD',
  6. });
  7. const getMedianPaycheck = pipe(
  8. pluck('salary'),
  9. filter((amount) => amount >= 100000),
  10. median,
  11. (amount) => amount / 12,
  12. toUSD
  13. );
  14. const result = getMedianPaycheck(employees);
  15. console.log({ result });

And R.lte is great for filtering the salaries.

  1. import { filter, lte, median, pipe, pluck } from 'ramda';
  2. import employees from './employees';
  3. const toUSD = (amount) => amount.toLocaleString('en-US', {
  4. style: 'currency',
  5. currency: 'USD',
  6. });
  7. const getMedianPaycheck = pipe(
  8. pluck('salary'),
  9. filter(lte(100000)),
  10. median,
  11. (amount) => amount / 12,
  12. toUSD
  13. );
  14. const result = getMedianPaycheck(employees);
  15. console.log({ result });

Ramda has a divide function, but it doesn’t work as expected.

  1. import { divide, filter, lte, median, pipe, pluck } from 'ramda';
  2. import employees from './employees';
  3. const toUSD = (amount) => amount.toLocaleString('en-US', {
  4. style: 'currency',
  5. currency: 'USD',
  6. });
  7. const getMedianPaycheck = pipe(
  8. pluck('salary'),
  9. filter(lte(100000)),
  10. median,
  11. divide(12),
  12. toUSD
  13. );
  14. const result = getMedianPaycheck(employees);
  15. console.log({ result });

$0.00?! That doesn’t look right. Let’s inspect with tap.

  1. import { divide, filter, lte, median, pipe, pluck, tap } from 'ramda';
  2. import employees from './employees';
  3. const toUSD = (amount) => amount.toLocaleString('en-US', {
  4. style: 'currency',
  5. currency: 'USD',
  6. });
  7. const getMedianPaycheck = pipe(
  8. pluck('salary'),
  9. filter(lte(100000)),
  10. median,
  11. tap((value) => {
  12. console.log('Before divide:', value);
  13. }),
  14. divide(12),
  15. tap((value) => {
  16. console.log('After divide:', value);
  17. }),
  18. toUSD
  19. );
  20. const result = getMedianPaycheck(employees);
  21. console.log({ result });

Aha! We’re dividing 12 by 608702.5 and getting a tiny decimal that rounds to $0.00! But we want to flip that division! Sounds like a job for Ramda’s flip function.

  1. import { divide, filter, flip, lte, median, pipe, pluck, tap } from 'ramda';
  2. import employees from './employees';
  3. const toUSD = (amount) => amount.toLocaleString('en-US', {
  4. style: 'currency',
  5. currency: 'USD',
  6. });
  7. const flippedDivide = flip(divide);
  8. const getMedianPaycheck = pipe(
  9. pluck('salary'),
  10. filter(lte(100000)),
  11. median,
  12. flippedDivide(12),
  13. toUSD
  14. );
  15. const result = getMedianPaycheck(employees);
  16. console.log({ result });

Looks good to me! flip takes a function and returns a new one with the first two arguments reversed.
https://ramdajs.com/docs/#flip
Again, I wouldn’t do this in the real world. The point’s to expose you to Ramda’s toolkit and let you decide what’s best for your application.