Guest User

Untitled

a guest
Jun 19th, 2018
95
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 4.76 KB | None | 0 0
  1. const evaluateWithOperators = ([...tokens], operators) => {
  2. let nextIndex
  3. while (~(nextIndex = indexOf(tokens, ...operators))) { {
  4. const evaluatePair = OPERATIONS[tokens[nextIndex]]
  5. const result = evaluatePair(tokens[nextIndex - 1], tokens[nextIndex + 1])
  6. // we mutate tokens directly here to get the next operator
  7. tokens.splice(nextIndex - 1, 3, result)
  8. }
  9.  
  10. return tokens
  11. }
  12.  
  13. const indexOfNextOperator = function * (tokens, operators) {
  14. let nextIndex
  15. while (~(nextIndex = indexOf(tokens, ...operators))) {
  16. yield nextIndex
  17. // the caller will take care of removing the found operator from tokens
  18. // probably not the best practice, but I think it's the clearest in this case
  19. }
  20. }
  21.  
  22. // Only evaluates operators specified in the operators array
  23. const evaluateWithOperators = ([...tokens], operators) => {
  24. for (const nextIndex of indexOfNextOperator(tokens, operators)) {
  25. const evaluatePair = OPERATIONS[tokens[nextIndex]]
  26. const result = evaluatePair(tokens[nextIndex - 1], tokens[nextIndex + 1])
  27. // we mutate tokens directly here to get the next operator
  28. tokens.splice(nextIndex - 1, 3, result)
  29. }
  30.  
  31. return tokens
  32. }
  33.  
  34. const MULTIPLY = '*'
  35. const ADD = '+'
  36. const SUBSTRACT = '-'
  37. const DIVIDE = '/'
  38. const CLOSE_PARENS = ')'
  39. const OPEN_PARENS = '('
  40. const OPERATIONS = {
  41. [MULTIPLY]: (a, b) => a * b,
  42. [ADD]: (a, b) => a + b,
  43. [SUBSTRACT]: (a, b) => a - b,
  44. [DIVIDE]: (a, b) => a / b
  45. }
  46. const ALL_OPERATORS = Object.keys(OPERATIONS)
  47.  
  48. const count = (arr, str) => arr.filter(ch => ch === str).length
  49. const indexOf = (str, ...searchValues) => str.findIndex(ch => searchValues.includes(ch))
  50. const push = (xs, ...x) => ((xs.push(...x), xs))
  51. const findLastIndex = ([...arr], fn) => {
  52. const index = arr.reverse().findIndex(fn)
  53. return ~index ? arr.length + ~index : -1
  54. }
  55.  
  56. const isAnOperator = str => ALL_OPERATORS.includes(str)
  57.  
  58. // gets the indices of all OPEN_PARENS
  59. const allOpenParens = expression =>
  60. [...expression]
  61. .reduce((indices, ch, index) =>
  62. ch === OPEN_PARENS ? push(indices, index) : indices
  63. , [])
  64.  
  65. const indexOfNextOperator = function * (tokens, operators) {
  66. let nextIndex
  67. while (~(nextIndex = indexOf(tokens, ...operators))) {
  68. yield nextIndex
  69. // the caller will take care of removing the found operator from tokens
  70. // probably not the best practice, but I think it's the clearest in this case
  71. }
  72. }
  73.  
  74. // Only evaluates operators specified in the operators array
  75. const evaluateWithOperators = ([...tokens], operators) => {
  76. for (const nextIndex of indexOfNextOperator(tokens, operators)) {
  77. const evaluatePair = OPERATIONS[tokens[nextIndex]]
  78. const result = evaluatePair(tokens[nextIndex - 1], tokens[nextIndex + 1])
  79. // we mutate tokens directly here to get the next operator
  80. tokens.splice(nextIndex - 1, 3, result)
  81. }
  82.  
  83. return tokens
  84. }
  85.  
  86. const getSign = operators => Math.pow(-1, count(operators, SUBSTRACT))
  87.  
  88. const countLastConsecutiveOperators = (tokens) =>
  89. tokens.length +
  90. ~findLastIndex(tokens, token => !ALL_OPERATORS.includes(token))
  91.  
  92. const reduceConsecutiveOperators = ([...tokens], number) => {
  93. const extraOperatorsCount = countLastConsecutiveOperators(tokens) - 1
  94. if (!extraOperatorsCount) return push(tokens, number)
  95. const operatorsToReduce = tokens.splice(-extraOperatorsCount)
  96.  
  97. return push(tokens, getSign(operatorsToReduce) * number)
  98. }
  99.  
  100. const evaluateLeadingOperators = tokens =>
  101. [SUBSTRACT, ADD].includes(tokens[0]) &&
  102. tokens.unshift(0)
  103.  
  104. const parseTokens = expression =>
  105. expression
  106. .match(/([0-9.]+)|([-,+,*,/])/g)
  107. .reduce((tokens, token) =>
  108. isAnOperator(token)
  109. ? push(tokens, token)
  110. : (evaluateLeadingOperators(tokens, +token),
  111. reduceConsecutiveOperators(tokens, +token))
  112. , [])
  113.  
  114. // evaluates an expression without parenthesis
  115. const evaluate = expression =>
  116. [[MULTIPLY, DIVIDE], [ADD, SUBSTRACT]]
  117. .reduce(evaluateWithOperators, parseTokens(expression))
  118. .pop()
  119.  
  120. const evaluateSubExpressionAt = (expression, openParens) => {
  121. const closeParens = expression.indexOf(CLOSE_PARENS, openParens)
  122. const subExpression = expression.slice(openParens + 1, closeParens)
  123. const result = evaluate(subExpression)
  124.  
  125. return expression.slice(0, openParens) +
  126. result +
  127. expression.slice(closeParens + 1)
  128. }
  129.  
  130. export const calc = expression =>
  131. // first evaluate everything in between open and closed parenthesis
  132. // they have to be evaluated in reverse order to make sure to always evaluate
  133. // the inner most expression
  134. evaluate(
  135. allOpenParens(expression)
  136. .reverse()
  137. .reduce(evaluateSubExpressionAt, expression)
  138. )
  139.  
  140.  
  141.  
  142. test('', () => {
  143. expect(calc('--1')).toBe(1)
  144. expect(calc('(--31+23)')).toBe(54)
  145. expect(calc('(--31+23+(----90-2/3)*-+---21-33)')).toBe(1897)
  146. expect(calc('((((((((((--23)))))))))*200)')).toBe(4600)
  147. })
Add Comment
Please, Sign In to add comment