如何使用功能性循环语句(map,forEach,reduce)而不是for循环来检查数组中是否存在两个元素之和。
例如这样的数组
DateComponents
我可以通过for循环来做到这一点:
Calendar
在这种情况下,有没有使用函数式循环语句而不是嵌套for循环的解决方案?
答案 0 :(得分:6)
简化的实现–
const main = (xs = []) =>
xs .some ((n, i) =>
xs .some ((m, j) =>
i < j && xs .includes (n + m)
)
)
console.log
( main ([ 1, 2, 4, 9, 4, 3 ]) // true
, main ([ 2, 7, 12, 6, 8, 20 ]) // true
, main ([ 1, 2, 4, 9 ]) // false
)
此优化使用Set
improves speed to O(1)
–
const main = (xs = [], s = new Set (xs)) =>
xs .some ((n, i) =>
xs .some ((m, j) =>
i < j && s .has (n + m)
)
)
console.log
( main ([ 1, 2, 4, 9, 4, 3 ]) // true
, main ([ 2, 7, 12, 6, 8, 20 ]) // true
, main ([ 1, 2, 4, 9 ]) // false
)
请记住仅在必要时进行优化
答案 1 :(得分:4)
编辑-我忘记了.some
提供每个元素的索引作为第二个参数。这样可以使它更干净!
const foo = [1, 2, 9, 4, 3]; // would return true
const bar = [1, 2, 4, 9]; // would return false
const baz = [2,7,12,6,8,20]; // should also be true
const sumOf2Exists = (arr) => {
// create a hash of the values in arr so we can check the
// accept condition in O(1) time
const hash = arr.reduce((acc, n) => {
acc[n] = true;
return acc;
}, {});
// find some n, m where n and m aren't the same entry
// and the sum n+m is in arr (using the hash)
return arr.some((n, i) => arr.some((m, j) => j > i && hash[n + m]));
};
console.log(sumOf2Exists(foo))
console.log(sumOf2Exists(bar))
console.log(sumOf2Exists(baz));
灵感来自tex的评论并使用Immutable.js
const { Set, Range } = Immutable
const foo = [1, 2, 9, 4, 3]; // would return true
const bar = [1, 2, 4, 9]; // would return false
const baz = [2, 7, 12, 6, 8, 20]; // should also be true
const sumOf2Exists = (arr) => {
const hash = Set(arr);
return arr.some((n, i) => (
Range(i+1, arr.length).some(j => hash.has(n + arr[j]))
));
};
console.log(sumOf2Exists(foo))
console.log(sumOf2Exists(bar))
console.log(sumOf2Exists(baz));
<script src="https://cdnjs.cloudflare.com/ajax/libs/immutable/3.8.2/immutable.min.js"></script>
答案 2 :(得分:2)
以下解决方案中的想法是使用reduce
和map
构建2个数字的所有可能组合,然后使用some
测试数组中是否存在任何组合的总和
function sumExists(arr) {
return arr
.reduce((acc, curr, i) => acc.concat(arr.slice(i + 1).map(e => [curr, e])), [])
.some(([a, b]) => arr.includes(a + b));
}
console.log(sumExists([1, 2, 9, 4, 3]));
console.log(sumExists([2, 7, 12, 6, 8, 20]));
console.log(sumExists([1, 2, 4, 9]));
O(n ^ 2)版本如下:
function sumExists(arr) {
const sums = new Set(arr);
return arr
.reduce((acc, curr, i) => {
acc.push(...arr.slice(i + 1).map(e => [curr, e]));
return acc;
}, [])
.some(([a, b]) => sums.has(a + b));
}
console.log(sumExists([1, 2, 9, 4, 3]));
console.log(sumExists([2, 7, 12, 6, 8, 20]));
console.log(sumExists([1, 2, 4, 9]));
答案 3 :(得分:1)
这是我使用Rxjs的解决方案:
import { Observable, from, of, zip, pairs, combineLatest, empty } from 'rxjs';
import { filter, map, takeUntil, takeWhile, single, zipAll, pairwise, combineAll, mergeMap, merge, first, skip, skipWhile, defaultIfEmpty } from 'rxjs/operators';
let test1 = [1, 2, 9, 4, 3]; // would return true as 1 + 2 = 3
let test2 = [2,7,12,6,8,20]; // true as 2 + 6 = 8 which is enough to make it true
let test3 = [1, 2, 4, 9]; //would return false
let observable1 = from(test1);
let skipSameIndex = (arr:number[], anchorIndex:number) => {
return from(arr).pipe(
filter((v, i) => {
// console.log('anchodIndex:', anchorIndex, ', i:', i);
return anchorIndex !== i;
})
)
}
let pairAll = (arr:number[]) => from(arr).pipe(mergeMap( (x, index) => {
return combineLatest(of(x), skipSameIndex(arr, index))
}));
let isPairExistsInArray = (pair:[number, number], arr: number[]) => {
let isExists = arr.indexOf(pair[0] + pair[1]) >= 0;
// console.log('pair:', pair, ', isExists:', isExists);
return isExists;
}
let isSumEachElementsExists = (arr:number[]) => pairAll(arr).pipe(
map((pair:[number, number]) => isPairExistsInArray(pair, arr)),
// first(isExists => isExists)
filter(x => x),
defaultIfEmpty(false)
);
// skipSameIndex(test3, 1).subscribe(x => console.log(x));
isSumEachElementsExists(test1).toPromise()
.then(isExists => console.log('test1 isExists:', isExists))
.catch(err => console.log('ERROR:', err));
isSumEachElementsExists(test2).toPromise()
.then(isExists => console.log('test2 isExists:', isExists))
.catch(err => console.log('ERROR:', err));
isSumEachElementsExists(test3).toPromise()
.then(isExists => console.log('test3 isExists:', isExists))
.catch(err => console.log('ERROR:', err));
在得出FP之后,我的结论很难,与迭代编程相比,解决方案过于复杂:)。如果有人可以简化我的工作,我愿意提出建议或纠正。
答案 4 :(得分:0)
我不确定这是否是正确的解决方案,因为我不太了解功能循环的含义,但请检查一下,此解决方案没有任何循环。
function check(list, i, j) {
if(i >= list.length || j >= list.length) return false;
if(i != j && list.includes(list[i] + list[j])) return true;
return check(list, i+1, j) || check(list, i, j+1)
}
check([1,2,9,4,3], 0, 0)
true
check([1,2, 4, 9], 0, 0)
false
答案 5 :(得分:0)
也许这是一种蛮力和过度设计的解决方案,但是我挑战自己提供一种纯净的JS纯粹功能的方法来解决问题。
顺便说一句,那里有Sanctuary或Ramda之类的库,或者还有许多其他库,这些库可以节省我的答案的一部分。
它只是一个问题:它检查所有可加组合(possibleSums
),但这是一个加号,因为可以打印出所有巧合。另外,someSumExists
仅检查possibleSums
是否输出一个或多个巧合。
另一方面,它还有另一个问题:它依赖于输入不包含重复数字的事实。
// ::: Combinators :::
// I :: a -> a -> a
const I = x => x
// T :: a -> (a -> a) -> a
const T = x => f => f (x)
// W :: a -> (a -> a -> a) -> a
const W = x => f => f (x) (x)
// ::: Non-generalized, partial Functor, Chain, Applicative Array-specific instances... :::
// map :: (a -> a) -> [a] -> [a]
const map = f => xs => xs.map (f)
// chain :: (a -> a) -> [a] -> [a]
const chain = f => xs => xs.reduce((o, x) => [...o, ...f (x)], [])
// ap :: [a -> b] -> [a] -> [b]
const ap = ms => m => chain (x => map (f => f(x)) (ms)) (m)
// lift2 :: [a -> c -> d] -> [a -> b] -> [b -> c] -> [d]
const lift2 = f => xs => ys => ap (map (f) (xs)) (ys)
// join :: [[a]] -> [a]
const join = chain (I)
// ::: Utils :::
// pipe :: [Any -> Any] -> Any -> Any
const pipe = xs => x => xs.reduce ((x, f) => f (x), x)
// any :: (a -> Boolean) -> [a] -> Boolean
const any = f => xs => xs.some (f)
// joinWith :: String -> [Any] -> [String]
const joinWith = sep => xs => xs.join (sep)
// length :: [Any] -> Number
const length = xs => xs.length
// equals :: a -> a -> Boolean
const equals = x => y => x === y
// not :: a -> Boolean
const not = x => x !== true
// possibleSums :: Number -> [[Number, Number, Number]]
const possibleSums = input =>
pipe ([
W,
T (lift2 (x => y => x !== y && input.includes (x + y) ? [[x, y, x + y]] : [])),
join
]) (input)
// someSumExists :: [Number] -> Boolean
const someSumExists = pipe ([
possibleSums,
length,
equals (0),
not
])
// printSums :: [[Number, Number, Number]] -> [String]
const printSums = pipe ([
map (([x, y, r]) => `${x} + ${y} = ${r}`),
joinWith ('\n')
])
// printPossibleSums :: [[Number, Number, Number]] -> String
const printPossibleSums = pipe ([ possibleSums, printSums ])
const input1 = [1, 2, 9, 4, 3]
const input2 = [2,7,12,6,8,20]
const input3 = [1, 2, 4, 9]
const output1 = printPossibleSums (input1)
const output1_ = someSumExists (input1)
const output2 = printPossibleSums (input2)
const output2_ = someSumExists (input2)
const output3 = printPossibleSums (input3)
const output3_ = someSumExists (input3)
console.log ('Input 1 has a case:', output1_)
console.log (output1)
console.log ('Input 2 has a case:', output2_)
console.log (output2)
console.log ('Input 3 has a case:', output3_)
console.log (output3)
使用Ramda函数保存代码行的方法与此相同:
const { map, join, length, equals, not, pipe, lift, unnest } = R
const W = x => f => f (x) (x)
const T = x => f => f (x)
const possibleSums = input =>
pipe (
W,
T (lift ((x, y) => x !== y && input.includes (x + y) ? [[x, y, x + y]] : [])),
unnest
) (input)
const someSumExists = pipe (
possibleSums,
length,
equals (0),
not
)
const printSums = pipe (
map (([x, y, r]) => `${x} + ${y} = ${r}`),
join ('\n')
)
const printPossibleSums = pipe (possibleSums, printSums)
const input1 = [1, 2, 9, 4, 3]
const input2 = [2,7,12,6,8,20]
const input3 = [1, 2, 4, 9]
const output1 = printPossibleSums (input1)
const output1_ = someSumExists (input1)
const output2 = printPossibleSums (input2)
const output2_ = someSumExists (input2)
const output3 = printPossibleSums (input3)
const output3_ = someSumExists (input3)
console.log ('Input 1 has a case:', output1_)
console.log (output1)
console.log ('Input 2 has a case:', output2_)
console.log (output2)
console.log ('Input 3 has a case:', output3_)
console.log (output3)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.min.js"></script>