map
。我需要将当前元素与前一个元素进行比较。我通过比较它们的
id
并根据此条件执行不同的操作来检测当前元素是否与前一个元素相同。是否有任何纯粹的函数式方法可以在不进行索引数学的情况下做到这一点?
items.map((item, index) => {
if(item.id === items[index - 1 > 0 ? index - 1 : 0].id) {
// do something
} else {
// do something else
}
})
代码有效,但我想避免对索引进行数学计算。有什么办法可以做到吗?
items.reduce((previousValue, currentValue) => {
if(currentValue.id === previousValue.id) {
// do something
} else {
// do something else
}
});
const None =
Symbol ()
const mapAdjacent = (f, [ a = None, b = None, ...more ] = []) =>
a === None || b === None
? []
: [ f (a, b), ...mapAdjacent (f, [ b, ...more ]) ]
const pair = (a, b) =>
[ a, b ]
console.log(mapAdjacent(pair, [ 1, 2, 3 ]))
// [ [ 1, 2 ], [ 2, 3 ] ]
console.log(mapAdjacent(pair, "hello"))
// [ [ h, e ], [ e, l ], [ l, l ], [ l, o ] ]
console.log(mapAdjacent(pair, [ 1 ]))
// []
console.log(mapAdjacent(pair, []))
// []
或者将其写为生成器 -
const mapAdjacent = function* (f, iter = [])
{ while (iter.length > 1)
{ yield f (...iter.slice(0,2))
iter = iter.slice(1)
}
}
const pair = (a, b) =>
[ a, b ]
console.log(Array.from(mapAdjacent(pair, [ 1, 2, 3 ])))
// [ [ 1, 2 ], [ 2, 3 ] ]
console.log(Array.from(mapAdjacent(pair, "hello")))
// [ [ h, e ], [ e, l ], [ l, l ], [ l, o ] ]
console.log(Array.from(mapAdjacent(pair, [ 1 ])))
// []
console.log(Array.from(mapAdjacent(pair, [])))
// []
reduce
。这是一个例子:
const input = [
{id: 1, value: "Apple Turnover"},
{id: 1, value: "Apple Turnover"},
{id: 2, value: "Banana Bread"},
{id: 3, value: "Chocolate"},
{id: 3, value: "Chocolate"},
{id: 3, value: "Chocolate"},
{id: 1, value: "Apple"},
{id: 4, value: "Danish"},
];
// Desired output: Array of strings equal to values in the above array,
// but with a prefix string of "New: " or "Repeated: " depending on whether
// the id is repeated or not
const reducer = (accumulator, currentValue) => {
let previousValue, descriptions, isRepeatedFromPrevious;
if (accumulator) {
previousValue = accumulator.previousValue;
descriptions = accumulator.descriptions;
isRepeatedFromPrevious = previousValue.id === currentValue.id;
} else {
descriptions = [];
isRepeatedFromPrevious = false;
}
if (isRepeatedFromPrevious) {
// The following line is not purely functional and performs a mutation,
// but maybe we do not care because the mutated object did not exist
// before this reducer ran.
descriptions.push("Repeated: " + currentValue.value);
} else {
// Again, this line is mutative
descriptions.push("New: " + currentValue.value);
}
return { previousValue: currentValue, descriptions }
};
const output = input.reduce(reducer, null).descriptions;
document.getElementById('output').innerText = JSON.stringify(output);
<output id=output></output>
XY 问题。如果您想映射数组的相邻元素,那么您必须定义自己的函数。
const mapAdjacent = (mapping, array) => {
const {length} = array, size = length - 1, result = new Array(size);
for (let i = 0; i < size; i++) result[i] = mapping(array[i], array[i + 1]);
return result;
};
const items = [1, 2, 3, 4, 5];
const result = mapAdjacent((x, y) => [x, y], items);
console.log(result); // [[1, 2], [2, 3], [3, 4], [4, 5]]
请注意,如果您给它一个空数组作为输入,这将抛出一个
RangeError
。
const mapAdjacent = (mapping, array) => {
const {length} = array, size = length - 1, result = new Array(size);
for (let i = 0; i < size; i++) result[i] = mapping(array[i], array[i + 1]);
return result;
};
const items = [];
const result = mapAdjacent((x, y) => [x, y], items); // RangeError: Invalid array length
console.log(result);
我认为这是一个很好的行为,因为你不应该一开始就给
mapAdjacent
一个空数组。
mapAdjacent
的纯函数式实现,它使用
reduceRight
。作为额外的好处,它适用于任何可迭代对象。
const mapAdjacent = (mapping, [head, ...tail]) =>
tail.reduceRight((recur, item) => prev =>
[mapping(prev, item), ...recur(item)]
, _ => [])(head);
const items = "hello";
const result = mapAdjacent((x, y) => [x, y], items);
console.log(result); // [['h', 'e'], ['e', 'l'], ['l', 'l'], ['l', 'o']]
与迭代版本不同,如果您将空数组作为输入,它会返回一个空数组,而不是抛出错误。
const mapAdjacent = (mapping, [head, ...tail]) =>
tail.reduceRight((recur, item) => prev =>
[mapping(prev, item), ...recur(item)]
, _ => [])(head);
const items = "";
const result = mapAdjacent((x, y) => [x, y], items);
console.log(result); // []
请注意,这是 JavaScript 中使用剩余元素进行数组解构的意外副作用。等效的 Haskell 版本确实会引发异常。
mapAdjacent :: (a -> a -> b) -> [a] -> [b]
mapAdjacent f (x:xs) = foldr (\y g x -> f x y : g y) (const []) xs x
main :: IO ()
main = do
print $ mapAdjacent (,) "hello" -- [('h','e'),('e','l'),('l','l'),('l','o')]
print $ mapAdjacent (,) "" -- Exception: Non-exhaustive patterns in function mapAdjacent
但是,此函数可能需要返回一个空数组。相当于在 Haskell 中添加
mapAdjacent f [] = []
情况。
pairWise
函数的实现:
function* pairWise(seq) {
if (!seq?.length) return;
let a = seq[0];
for (let i = 1; i < seq.length; i++) {
const b = seq[i];
yield [a, b];
a = b;
}
}
用途:
[...pairWise([1, 2, 3, 4, 5])] // [[1, 2], [2, 3], [3, 4], [4, 5]]
.map(([item1, item2], index) => null);
[...pairWise('hi!')] // [['h, 'i'], ['i', '!']]
pairWise 本身并不是以纯函数方式实现的。也许可以,但我懒得打扰。