幾個關於js陣列方法reduce的經典片段
阿新 • • 發佈:2019-01-28
以下是個人在工作中收藏總結的一些關於javascript陣列方法reduce
的相關程式碼片段,後續遇到其他使用這個函式的場景,將會陸續新增,這裡作為備忘。
javascript陣列那麼多方法,為什麼我要單挑reduce
方法,一個原因是我對這個方法掌握不夠,不能夠用到隨心所欲。另一個方面,我也感覺到了這個方法的龐大魅力,在許多的場景中發揮著神奇的作用。
理解reduce
函式
reduce() 方法接收一個函式作為累加器(accumulator),陣列中的每個值(從左到右)開始縮減,最終為一個值。
arr.reduce([callback, initialValue])
關於reduce
看如下例子:
let arr = [1, 2, 3, 4, 5];
// 10代表初始值,p代表每一次的累加值,在第一次為10
// 如果不存在初始值,那麼p第一次值為1
// 此時累加的結果為15
let sum = arr.reduce((p, c) => p + c, 10); // 25
// 轉成es5的寫法即為:
var sum = arr.reduce(function(p, c) {
console.log(p);
return p + c;
}, 10);
片段一:字母遊戲
const anagrams = str => {
if (str.length <= 2) {
return str.length === 2 ? [str, str[1] + str[0]] : str;
}
return str.split("").reduce((acc, letter, i) => {
return acc.concat(anagrams(str.slice(0, i) + str.slice(i + 1)).map(val => letter + val));
}, []);
}
anagrams("abc"); // 結果會是什麼呢?
reduce
片段二:累加器
const sum = arr => arr.reduce((acc, val) => acc + val, 0);
sum([1, 2, 3]);
片段三:計數器
const countOccurrences = (arr, value) => arr.reduce((a, v) => v === value ? a + 1 : a + 0, 0);
countOccurrences([1, 2, 3, 2, 2, 5, 1], 1);
迴圈陣列,每遇到一個值與給定值相等,即加1,同時將加上之後的結果作為下次的初始值。
片段四:函式柯里化
const curry = (fn, arity = fn.length, ...args) =>
arity <= args.length ? fn(...args) : curry.bind(null, fn, arity, ...args);
curry(Math.pow)(2)(10);
curry(Math.min, 3)(10)(50)(2);
通過判斷函式的引數取得當前函式的length
(當然也可以自己指定),如果所傳的引數比當前引數少,則繼續遞迴下面,同時儲存上一次傳遞的引數。
片段五:陣列扁平化
const deepFlatten = arr =>
arr.reduce((a, v) => a.concat(Array.isArray(v) ? deepFlatten(v) : v), []);
deepFlatten([1, [2, [3, 4, [5, 6]]]]);
片段六:生成菲波列契陣列
const fibonacci = n => Array(n).fill(0).reduce((acc, val, i) => acc.concat(i > 1 ? acc[i - 1] + acc[i - 2] : i), []);
fibonacci(5);
片段七:管道加工器
const pipe = (...funcs) => arg => funcs.reduce((acc, func) => func(acc), arg);
pipe(btoa, x => x.toUpperCase())("Test");
通過對傳遞的引數進行函式加工,之後將加工之後的資料作為下一個函式的引數,這樣層層傳遞下去。
片段八:中介軟體
const dispatch = action => {
console.log('action', action);
return action;
}
const middleware1 = dispatch => {
return action => {
console.log("middleware1");
const result = dispatch(action);
console.log("after middleware1");
return result;
}
}
const middleware2 = dispatch => {
return action => {
console.log("middleware2");
const result = dispatch(action);
console.log("after middleware2");
return result;
}
}
const middleware3 = dispatch => {
return action => {
console.log("middleware3");
const result = dispatch(action);
console.log("after middleware3");
return result;
}
}
const compose = middlewares => middlewares.reduce((a, b) => args => a(b(args)))
const middlewares = [middleware1, middleware2, middleware3];
const afterDispatch = compose(middlewares)(dispatch);
const testAction = arg => {
return { type: "TEST_ACTION", params: arg };
};
afterDispatch(testAction("1111"));
redux
中經典的compose
函式中運用了這種方式,通過對中介軟體的重重層疊,在真正發起action的時候觸發函式執行。
片段九:redux-actions對state的加工片段
// redux-actions/src/handleAction.js
const handleAction = (type, reducer, defaultState) => {
const types = type.toString();
const [nextReducer, throwReducer] = [reducer, reducer];
return (state = defaultState, action) => {
const { type: actionType } = action;
if (!actionType || types.indexOf(actionType.toString()) === -1) {
return state;
}
return (action.error === true ? throwReducer : nextReducer)(state, action);
}
}
// reduce-reducers/src/index.js
const reduceReducer = (...reducers) => {
return (previous, current) => {
reducers.reduce((p, r) => r(p, current), previous);
}
}
// redux-actions/src/handleActions.js
const handleActions = (handlers, defaultState, { namespace } = {}) => {
// reducers的扁平化
const flattenedReducerMap = flattenReducerMap(handles, namespace);
// 每一種ACTION下對應的reducer處理方式
const reducers = Reflect.ownkeys(flattenedReducerMap).map(type => handleAction(
type,
flattenedReducerMap[type],
defaultState
));
// 狀態的加工器,用於對reducer的執行
const reducer = reduceReducers(...reducers);
// reducer觸發
return (state = defaultState, action) => reducer(state, action);
}
片段十:資料加工器
const reducers = {
totalInEuros: (state, item) => {
return state.euros += item.price * 0.897424392;
},
totalInYen: (state, item) => {
return state.yens += item.price * 113.852;
}
};
const manageReducers = reducers => {
return (state, item) => {
return Object.keys(reducers).reduce((nextState, key) => {
reducers[key](state, item);
return state;
}, {})
}
}
const bigTotalPriceReducer = manageReducers(reducers);
const initialState = { euros: 0, yens: 0 };
const items = [{ price: 10 }, { price: 120 }, { price: 1000 }];
const totals = items.reduce(bigTotalPriceReducer, initialState);
片段十一:物件空值判斷
let school = {
name: 'Hope middle school',
created: '2001',
classes: [
{
name: '三年二班',
teachers: [
{ name: '張二蛋', age: 26, sex: '男', actor: '班主任' },
{ name: '王小妞', age: 23, sex: '女', actor: '英語老師' }
]
},
{
name: '明星班',
teachers: [
{ name: '歐陽娜娜', age: 29, sex: '女', actor: '班主任' },
{ name: '李易峰', age: 28, sex: '男', actor: '體育老師' },
{ name: '楊冪', age: 111, sex: '女', actor: '藝術老師' }
]
}
]
};
// 常規做法
school.classes &&
school.classes[0] &&
school.classes[0].teachers &&
school.classes[0].teachers[0] &&
school.classes[0].teachers[0].name
// reduce方法
const get = (p, o) => p.reduce((xs, x) => (xs && xs[x] ? xs[x] : null), o);
get(['classes', 0, 'teachers', 0, 'name'], school); // 張二蛋
片段十二:分組
const groupBy = (arr, func) =>
arr.map(typeof func === 'function' ? func : val => val[func]).reduce((acc, val, i) => {
acc[val] = (acc[val] || []).concat(arr[i]);
return acc;
}, {});
groupBy([6.1, 4.2, 6.3], Math.floor);
groupBy(['one', 'two', 'three'], 'length');
首先通過map
計算出所有的鍵值,然後再根據建值進行歸類
片段十三:物件過濾
const pick = (obj, arr) =>
arr.reduce((acc, curr) => (curr in obj && (acc[curr] = obj[curr]), acc), {});
pick({ a: 1, b: '2', c: 3 }, ['a', 'c']);
根據給出的鍵值來遍歷,比較物件中是否存在相同鍵值的的值,然後通過逗號表示式把賦值後的物件賦給下一個的初始值
片段十四:陣列中刪除指定位置的值
const remove = (arr, func) =>
Array.isArray(arr)
? arr.filter(func).reduce((acc, val) => {
arr.splice(arr.indexOf(val), 1);
return acc.concat(val);
}, []) : [];
const arr = [1, 2, 3, 4];
remove(arr, n => n % 2 == 0);
首先根據filter
函式過濾出陣列中符合條件的值,然後使用reduce
在原陣列中刪除符合條件的值,可以得出最後arr的值變成了[1, 3]
片段十五:promise按照順序執行
const runPromisesInSeries = ps => ps.reduce((p, next) => p.then(next), Promise.resolve());
const delay = d => new Promise(r => setTimeout(r, d));
const print = args => new Promise(r => r(args));
runPromisesInSeries([() => delay(1000), () => delay(2000), () => print('hello')]);
片段十六:排序
const orderBy = (arr, props, orders) =>
[...arr].sort((a, b) =>
props.reduce((acc, prop, i) => {
if (acc === 0) {
const [p1, p2] = orders && orders[i] === 'desc' ? [b[prop], a[prop]] : [a[prop], b[prop]];
acc = p1 > p2 ? 1 : p1 < p2 ? -1 : 0;
}
return acc;
}, 0)
);
const users = [{ name: 'fred', age: 48 }, { name: 'barney', age: 36 }, { name: 'fly', age: 26 }];
orderBy(users, ['name', 'age'], ['asc', 'desc']);
orderBy(users, ['name', 'age']);
片段十七:選擇
const select = (from, selector) =>
selector.split('.').reduce((prev, cur) => prev && prev[cur], from);
const obj = { selector: { to: { val: 'val to select' } } };
select(obj, 'selector.to.val');