乐闻世界logo
搜索文章和话题

What functional programming tools are available in Lodash? Please provide examples of their usage

2月18日 22:01

Lodash provides rich functional programming tools. Here is a detailed answer about Lodash functional programming:

Lodash Functional Programming Overview

Lodash provides many functional programming tools, including function composition, currying, memoization, and more. These tools help developers write more concise and maintainable code.

1. Function Composition

_.flow([funcs])

Creates a function that executes the given functions from left to right.

javascript
function square(n) { return n * n; } function addTwo(n) { return n + 2; } var addSquare = _.flow(addTwo, square); addSquare(3); // => 25 // Real application: Data transformation pipeline function processData(data) { return _.flow( filterActive, transformData, sortByDate )(data); } function filterActive(items) { return _.filter(items, item => item.active); } function transformData(items) { return _.map(items, item => ({ id: item.id, name: _.upperFirst(item.name), value: _.round(item.value, 2) })); } function sortByDate(items) { return _.orderBy(items, ['createdAt'], ['desc']); }

_.flowRight([funcs])

Creates a function that executes the given functions from right to left (similar to function composition).

javascript
function square(n) { return n * n; } function addTwo(n) { return n + 2; } var addSquare = _.flowRight(square, addTwo); addSquare(3); // => 25 // Real application: Validation and processing function validateAndProcess(input) { return _.flowRight( processResult, validateInput, sanitizeInput )(input); } function sanitizeInput(input) { return _.trim(input); } function validateInput(input) { if (!input) throw new Error('Invalid input'); return input; } function processResult(input) { return _.upperFirst(input); }

2. Function Currying

_.curry(func, [arity=func.length])

Creates a function that accepts one or more arguments. If the number of arguments provided is less than arity, it returns a function that accepts the remaining arguments.

javascript
var abc = function(a, b, c) { return [a, b, c]; }; var curried = _.curry(abc); curried(1)(2)(3); // => [1, 2, 3] curried(1, 2)(3); // => [1, 2, 3] curried(1, 2, 3); // => [1, 2, 3] // Real application: Create reusable functions const multiply = (a, b) => a * b; const double = _.curry(multiply)(2); const triple = _.curry(multiply)(3); console.log(double(5)); // => 10 console.log(triple(5)); // => 15 // Real application: API requests const fetchData = (baseUrl, endpoint, params) => { return fetch(`${baseUrl}/${endpoint}?${new URLSearchParams(params)}`); }; const fetchFromAPI = _.curry(fetchData)('https://api.example.com'); const fetchUsers = fetchFromAPI('users'); const fetchPosts = fetchFromAPI('posts'); fetchUsers({ page: 1, limit: 10 }); fetchPosts({ userId: 123 });

_.curryRight(func, [arity=func.length])

Similar to _.curry, but arguments are applied from right to left.

javascript
var abc = function(a, b, c) { return [a, b, c]; }; var curried = _.curryRight(abc); curried(3)(2)(1); // => [1, 2, 3] curried(3, 2)(1); // => [1, 2, 3] curried(3, 2, 1); // => [1, 2, 3]

_.partial(func, [partials])

Creates a function that invokes func with partial arguments prepended.

javascript
var greet = function(greeting, punctuation, name) { return greeting + ' ' + name + punctuation; }; var sayHelloTo = _.partial(greet, 'hello', '!'); var sayHelloToFred = sayHelloTo('fred'); sayHelloToFred; // => 'hello fred!' // Real application: Create functions with specific configuration const fetchWithConfig = _.partial(fetch, { headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer token' } }); fetchWithConfig('https://api.example.com/data');

_.partialRight(func, [partials])

Similar to _.partial, but partial arguments are appended.

javascript
var greet = function(greeting, name, punctuation) { return greeting + ' ' + name + punctuation; }; var greetFred = _.partialRight(greet, 'fred'); var sayHelloToFred = _.partialRight(greetFred, '!'); sayHelloToFred('hello'); // => 'hello fred!'

3. Function Memoization

_.memoize(func, [resolver])

Creates a memoized function that caches function results.

javascript
var object = { 'a': 1, 'b': 2 }; var other = { 'c': 3, 'd': 4 }; var values = _.memoize(_.values); values(object); // => [1, 2] values(other); // => [3, 4] object.a = 2; values(object); // => [1, 2] // cached result // Real application: Cache calculation results function fibonacci(n) { if (n <= 1) return n; return fibonacci(n - 1) + fibonacci(n - 2); } const memoizedFibonacci = _.memoize(fibonacci); console.time('fibonacci'); console.log(fibonacci(40)); // slow console.timeEnd('fibonacci'); console.time('memoized'); console.log(memoizedFibonacci(40)); // fast console.timeEnd('memoized'); // Real application: Cache API responses const fetchUser = _.memoize(async (userId) => { const response = await fetch(`/api/users/${userId}`); return response.json(); }, userId => userId); // First call sends request const user1 = await fetchUser(1); // Second call returns cache const user2 = await fetchUser(1);

4. Function Wrapping

_.wrap(value, [wrapper=identity])

Creates a function that provides value to the wrapper as its first argument.

javascript
var p = _.wrap(_.escape, function(func, text) { return '<p>' + func(text) + '</p>'; }); p('fred, barney, & pebbles'); // => '<p>fred, barney, &amp; pebbles</p>' // Real application: Wrap logging function const logWithTimestamp = _.wrap(console.log, (fn, ...args) => { const timestamp = new Date().toISOString(); fn(`[${timestamp}]`, ...args); }); logWithTimestamp('User logged in', { userId: 123 }); // => [2024-01-01T00:00:00.000Z] User logged in { userId: 123 }

_.negate(predicate)

Creates a function that negates the result of predicate.

javascript
function isEven(n) { return n % 2 === 0; } _.filter([1, 2, 3, 4, 5, 6], _.negate(isEven)); // => [1, 3, 5] // Real application: Filter conditions const isActive = user => user.active; const isInactive = _.negate(isActive); const activeUsers = _.filter(users, isActive); const inactiveUsers = _.filter(users, isInactive);

5. Function Utilities

_.ary(func, [n=func.length])

Creates a function that invokes func with at most n arguments.

javascript
_.map(['6', '8', '10'], _.ary(parseInt, 1)); // => [6, 8, 10] // Real application: Limit argument count const logFirstTwo = _.ary(console.log, 2); logFirstTwo('a', 'b', 'c'); // only outputs 'a', 'b'

_.unary(func)

Creates a function that accepts only one argument.

javascript
_.map(['6', '8', '10'], _.unary(parseInt)); // => [6, 8, 10] // Real application: Ensure function accepts only one argument const logFirst = _.unary(console.log); logFirst('a', 'b', 'c'); // only outputs 'a'

_.once(func)

Creates a function that is restricted to invoking func once.

javascript
var initialize = _.once(createApplication); initialize(); initialize(); // `createApplication` is invoked only once // Real application: Singleton initialization let database = null; const getDatabase = _.once(() => { database = connectToDatabase(); return database; }); const db1 = getDatabase(); const db2 = getDatabase(); console.log(db1 === db2); // true

_.before(n, func)

Creates a function that invokes func at most n times.

javascript
_.before(2, function() { console.log('called'); })(); // => logs 'called' _.before(2, function() { console.log('called'); })(); // => logs 'called' _.before(2, function() { console.log('called'); })(); // => does not log // Real application: Limit call count const logFirstThree = _.before(3, console.log); logFirstThree('1'); // logs logFirstThree('2'); // logs logFirstThree('3'); // logs logFirstThree('4'); // does not log

_.after(n, func)

Creates a function that invokes func after it has been called n times.

javascript
var saves = ['profile', 'settings']; var done = _.after(saves.length, function() { console.log('done saving!'); }); _.forEach(saves, function(type) { asyncSave({ type: type, complete: done }); }); // => logs 'done saving!' after the two async saves have completed // Real application: Wait for multiple async operations to complete const waitForAll = (count, callback) => { let remaining = count; return _.after(count, () => callback()); }; const done = waitForAll(3, () => { console.log('All tasks completed'); }); // Simulate async tasks setTimeout(() => done(), 100); setTimeout(() => done(), 200); setTimeout(() => done(), 300);

_.spread(func, [start=0])

Creates a function that invokes func with an array of arguments.

javascript
var say = _.spread(function(who, what) { return who + ' says ' + what; }); say(['fred', 'hello']); // => 'fred says hello' // Real application: Handle array arguments const max = _.spread(Math.max); console.log(max([1, 2, 3, 4, 5])); // => 5 const sum = _.spread((...args) => _.sum(args)); console.log(sum([1, 2, 3, 4, 5])); // => 15

Real-World Application Examples

Functional Data Processing Pipeline

javascript
class DataPipeline { constructor() { this.pipeline = []; } pipe(func) { this.pipeline.push(func); return this; } process(data) { return _.flow(...this.pipeline)(data); } } const pipeline = new DataPipeline() .pipe(data => _.filter(data, item => item.active)) .pipe(data => _.map(data, item => ({ id: item.id, name: _.upperFirst(item.name), value: _.round(item.value, 2) }))) .pipe(data => _.orderBy(data, ['createdAt'], ['desc'])) .pipe(data => _.take(data, 10)); const result = pipeline.process(rawData);

Functional API Client

javascript
class APIClient { constructor(baseUrl) { this.baseUrl = baseUrl; } request = _.curry((method, endpoint, params) => { const url = `${this.baseUrl}${endpoint}`; const options = { method, headers: { 'Content-Type': 'application/json' } }; if (method === 'GET') { url += '?' + new URLSearchParams(params); } else { options.body = JSON.stringify(params); } return fetch(url, options).then(res => res.json()); }); get = this.request('GET'); post = this.request('POST'); put = this.request('PUT'); delete = this.request('DELETE'); getUsers = this.get('/users'); getUser = this.get('/users/:id'); createUser = this.post('/users'); updateUser = this.put('/users/:id'); deleteUser = this.delete('/users/:id'); } const api = new APIClient('https://api.example.com'); api.getUsers({ page: 1, limit: 10 }); api.getUser({ id: 123 }); api.createUser({ name: 'John', email: 'john@example.com' });

Functional Validator

javascript
class Validator { constructor() { this.validators = []; } add(validator) { this.validators.push(validator); return this; } validate(data) { const results = this.validators.map(validator => validator(data)); const errors = _.filter(results, result => !result.valid); return { valid: errors.length === 0, errors: _.flatMap(errors, error => error.errors) }; } static required(field) { return data => ({ valid: !_.isEmpty(data[field]), errors: _.isEmpty(data[field]) ? [`${field} is required`] : [] }); } static minLength(field, length) { return data => ({ valid: data[field] && data[field].length >= length, errors: !data[field] || data[field].length < length ? [`${field} must be at least ${length} characters`] : [] }); } static email(field) { const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; return data => ({ valid: emailRegex.test(data[field]), errors: !emailRegex.test(data[field]) ? [`${field} must be a valid email`] : [] }); } } const userValidator = new Validator() .add(Validator.required('name')) .add(Validator.minLength('name', 2)) .add(Validator.required('email')) .add(Validator.email('email')); const result = userValidator.validate({ name: 'John', email: 'john@example.com' });

Summary

Lodash provides rich functional programming tools, including:

  1. Function Composition: _.flow, _.flowRight - Combine multiple functions into one
  2. Function Currying: _.curry, _.curryRight, _.partial, _.partialRight - Pre-fill partial arguments
  3. Function Memoization: _.memoize - Cache function results to improve performance
  4. Function Wrapping: _.wrap, _.negate - Wrap or modify function behavior
  5. Function Utilities: _.ary, _.unary, _.once, _.before, _.after, _.spread - Control function invocation

Mastering these functional programming tools can help developers write more concise, maintainable, and efficient code. In actual development, it's recommended to choose appropriate tools based on specific needs and make full use of the advantages of functional programming.

标签:Lodash