In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-29 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/02 Report--
This article is to share with you about how to use functional TypeScript code, the editor thinks it is very practical, so I share it with you to learn. I hope you can get something after reading this article.
When it comes to functional programming, we often talk about mechanisms and methods rather than core principles. Functional programming is not about concepts like Monad, Monoid, and Zipper, although they are really useful. Fundamentally, functional programming is about combinatorial programming such as using general reusable functions. This article is the result of some of my thinking about using functionalism when refactoring TypeScript code.
First, we need to use the following technologies:
Use functions instead of simple values whenever possible
Pipelinization of data conversion process
Extract general function
Come on, let's go!
Suppose we have two classes, Employee and Department. Employee has name and salary attributes, and Department is just a simple collection of Employee.
Class Employee {constructor (public name: string, public salary: number) {} class Department {constructor (public employees: Employee []) {} works (employee: Employee): boolean {return this.employees.indexOf (employee) >-1;}}
What we are going to ReFactor is the averageSalary function.
Function averageSalary (employees: Employee [], minSalary: number, department?: Department): number {let total = 0; let count = 0; employees.forEach ((e) = > {if (minSalary {const empls = [new Employee ("Jim", 100), new Employee ("John", 200), new Employee ("Liz", 120), new Employee ("Penny", 30)]; const sales = new Department ([empls [0], empls [1]) It ("calculates the average salary", () = > {expect (averageSalary (empls, 50, sales)) .toEqual; expect (averageSalary (empls, 50)) .toEqual;})
The requirements are simple and crude, but even without mentioning that the code is difficult to expand, the confusion is obvious. If new conditions are added, function signatures and interfaces will have to be changed, and if statements will become more and more bloated and scary.
Let's reconstruct this function with some functional programming.
Use functions instead of simple values
Using functions instead of simple values may not seem intuitive, but it's a powerful way to organize inductive code. In our example, doing so means replacing the minSalary and department parameters with functions that are checked by two conditions.
Type Predicate = (e: Employee) = > boolean; function averageSalary (employees: Employee [], salaryCondition: Predicate, departmentCondition?: Predicate): number {let total = 0; let count = 0; employees.forEach ((e) = > {if (salaryCondition (e) & (departmentCondition = undefined | | departmentCondition (e)) {total + = e.salt; count + = 1;}}); return total = = 0? 0: total / count;} / /. Expect (averageSalary (empls, (e) = > e.salary > 50, (e) = > sales.works (e) .toEqual
What we do is unify the two conditional interfaces of salary and department. Previously, these two conditions were written to death, but now they are clearly defined and follow a consistent interface. This integration allows us to pass all the conditions as an array.
Function averageSalary (employees: Employee [], conditions: Predicate []): number {let total = 0; let count = 0; employees.forEach ((e) = > {if (conditions.every (c = > c (e) {total + = e.salt; count + = 1;}}); return (count = 0)? 0: total / count;} / /. Expect (averageSalary (empls, [(e) = > e.salary > 50, (e) = > sales.works (e)]) .toEqual (150)
Conditional arrays are just combined conditions, and you can put them together with a simple combiner to make them look clearer.
Function and (predicates: Predicate []): Predicate {return (e) = > predicates.every (p = > p (e));} function averageSalary (employees: Employee [], conditions: Predicate []): number {let total = 0; let count = 0; employees.forEach ((e) = > {if (and (conditions) (e)) {total + = e.salt; count + = 1;}); return (count = 0)? 0: total / count;}
It is worth noting that the "and" combiner is generic, can be reused, and may also be expanded into libraries.
Bring up the result
The averageSalary function is now much more robust. We can add new conditions without breaking the function interface or changing the function implementation.
Pipelinization of data conversion process
Another useful practice of functional programming is to turn all data conversion processes into pipes. In this case, the filter process is extracted outside the loop.
Function averageSalary (employees: Employee [], conditions: Predicate []): number {const filtered = employees.filter (and (conditions)); let total = 0 let count = 0 filtered.forEach ((e) = > {total + = e.salt; count + = 1;}); return (count = = 0)? 0: total / count;}
In this way, it is useless to count count.
Function averageSalary (employees: Employee [], conditions: Predicate []): number {const filtered = employees.filter (and (conditions)); let total = 0 filtered.forEach ((e) = > {total + = e.salt;}); return (filtered.length = = 0)? 0: total / filtered.length;}
Next, if the salary is extracted before the stack, the summation process becomes a simple reduce.
Function averageSalary (employees: Employee [], conditions: Predicate []): number {const filtered = employees.filter (and (conditions)); const salaries = filtered.map (e = > e.salary); const total = salaries.reduce ((total b) = > a + b, 0); return (salaries.length = = 0)? 0: total / salaries.length;}
Extract general function
Then we found that the two lines of code had nothing to do with the current domain. It does not contain any information related to employees and departments. It's just a function that calculates the average. So extract it, too.
Function average (nums: number []): number {const total = nums.reduce ((aPowerb) = > a + b, 0); return (nums.length = = 0)? 0: total / nums.length;} function averageSalary (employees: Employee [], conditions: Predicate []): number {const filtered = employees.filter (and (conditions)); const salaries = filtered.map (e = > e.salary); return average (salaries);}
Once again, the extracted function is completely generic.
After bringing up all the salary parts, we have a solution.
Function employeeSalaries (employees: Employee [], conditions: Predicate []): number [] {const filtered = employees.filter (and (conditions)); return filtered.map (e = > e.salary);} function averageSalary (employees: Employee [], conditions: Predicate []): number {return average (employeeSalaries (employees, conditions);}
Comparing the original plan with the * plan, I dare say that there is no doubt that the latter is better. First of all, it is more generic (we can add a new type of judgment condition without breaking the function interface). Second, we are freed from mutable state (mutable state) and if statements, which makes the code easier to read and understand.
When to stop?
In functional programming, we write a lot of small functions that take a collection and return a new collection. These functions can be combined and reused in different ways-- great. One drawback of this style, however, is that the code can become too abstract to read. What exactly are those functions doing together?
I like to use the Lego analogy: Lego bricks can be put together in different forms; they can be combined. But note that not all building blocks are small pieces. Therefore, when using the techniques described in this article for code refactoring, don't try to turn everything into a function that receives and returns an array. Admittedly, some of these function combinations are extremely easy to use, but they can also significantly reduce our ability to understand programs.
Here is how to ReFactor TypeScript code using functional thinking. What I follow are the following rules:
Use functions instead of simple values whenever possible
Pipelinization of data conversion process
Extract general function
The above is how to use functional TypeScript code, the editor believes that there are some knowledge points that we may see or use in our daily work. I hope you can learn more from this article. For more details, please follow the industry information channel.
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.