Repository: char0n/ramda-adjunct (js)

Issue:
overlaps
**Is your feature request related to a problem? Please describe.**

```js
// overlaps :: [a] -> [a] -> Boolean
const overlaps = R.pipe(R.intersection, R.complement(R.isEmpty));
process.argv; //=> ['node', 'script.js', '-v']
overlaps(['-v', '--verbose'], process.argv); //=> true
```

**Describe the solution you'd like**

Do any of these strings appear in another list?

**Additional context**

This function is part of [ramda cookbook](https://github.com/ramda/ramda/wiki/Cookbook#problem-do-any-of-these-strings-appear-in-another-list). 

- [ ] implement the function
- [ ] add ref to ramda cookbook

Pull request description:
feat(relation): add overlaps
Closes #1229


Patch:
diff --git a/src/index.js b/src/index.js
index a11cda7c25..ad6c38ebc2 100644
--- a/src/index.js
+++ b/src/index.js
@@ -190,6 +190,7 @@ export { default as propNotEq } from './propNotEq';
 export { default as pathNotEq } from './pathNotEq';
 export { default as inRange } from './inRange';
 export { default as notEqual } from './notEqual';
+export { default as overlaps } from './overlaps';
 // Logic
 export { default as isNotEmpty } from './isNotEmpty';
 export { default as defaultWhen } from './defaultWhen';
diff --git a/src/overlaps.js b/src/overlaps.js
new file mode 100644
index 0000000000..71002fa831
--- /dev/null
+++ b/src/overlaps.js
@@ -0,0 +1,32 @@
+import { pipe, intersection, isEmpty, curryN } from 'ramda';
+
+import isNotEmpty from './isNotEmpty';
+
+/**
+ * Returns true if two lists have at least one element common to both lists.
+ *
+ * @func overlaps
+ * @memberOf RA
+ * @category Relation
+ * @since {@link https://char0n.github.io/ramda-adjunct/2.30.0|v2.30.0}
+ * @sig [a] -> [a] -> Boolean
+ * @param {Array} list1 The first list
+ * @param {Array} list2 The second list
+ * @return {boolean} True if two lists have at least one element common to both lists
+ * @example
+ *
+ * RA.overlaps(['-v', '--verbose'], ['node', 'script.js', '-v']); //=> true
+ * RA.overlaps(['-v', '--verbose'], []); //=> false
+ * RA.overlaps([1, 2, 3], [3, 4, 5]); //=> true
+ * RA.overlaps([1, 2, 3], [4, 5]); //=> false
+ */
+
+const overlaps = curryN(2, (list1, list2) => {
+  if (isEmpty(list1)) {
+    return true;
+  }
+
+  return pipe(intersection, isNotEmpty)(list1, list2);
+});
+
+export default overlaps;
diff --git a/types/index.d.ts b/types/index.d.ts
index e8fd55f10f..910ff03f07 100644
--- a/types/index.d.ts
+++ b/types/index.d.ts
@@ -1457,6 +1457,12 @@ declare namespace RamdaAdjunct {
             (from: number, to: number): number[];
             (from: number): (to: number) => number[];
         };
+
+        /**
+         * Returns true if two lists have at least one element common to both lists.
+         */
+        overlaps<T>(list1: T[], list2: T[]): boolean;
+        overlaps<T>(list1: T[]): (list2: T[]) => boolean;
     }
 }
 


Test patch:
diff --git a/test/overlaps.js b/test/overlaps.js
new file mode 100644
index 0000000000..e98906dfc0
--- /dev/null
+++ b/test/overlaps.js
@@ -0,0 +1,68 @@
+import { assert } from 'chai';
+import * as R from 'ramda';
+
+import * as RA from '../src';
+
+describe('overlaps', function () {
+  context('given two intersecting lists', function () {
+    specify('should return true', function () {
+      assert.isTrue(RA.overlaps([1], [1]));
+      assert.isTrue(RA.overlaps([1, 2], [1]));
+      assert.isTrue(RA.overlaps([1, 2], [2]));
+      assert.isTrue(RA.overlaps([1, 2], [1, 2]));
+      assert.isTrue(RA.overlaps([1, null], [1]));
+      assert.isTrue(RA.overlaps([1, null], [null]));
+      assert.isTrue(RA.overlaps([undefined], [undefined, null]));
+      assert.isTrue(RA.overlaps([{}, []], [{}]));
+      assert.isTrue(RA.overlaps([{}, [1, 2]], [[1, 2]]));
+      assert.isTrue(RA.overlaps([{}, []], [[]]));
+      assert.isTrue(RA.overlaps([{ key: 'value' }, []], [{ key: 'value' }]));
+      assert.isTrue(
+        RA.overlaps(['-v', '--verbose'], ['node', 'script.js', '-v'])
+      );
+    });
+  });
+
+  context('given lists of intersecting functions', function () {
+    specify('should find overlap', function () {
+      /**
+       * In ramda@0.22.x, R.intersection function returns empty results
+       * for intersecting functions. We assume it's probably a bug,
+       * so we compensate for it with our test code.
+       */
+      const fn = () => {};
+      const actual = RA.overlaps([undefined, fn], [fn]);
+      const expected = R.intersection([fn], [fn]);
+
+      assert.strictEqual(actual, expected.length === 1);
+    });
+  });
+
+  context('given two disjunctive lists', function () {
+    specify('should return false', function () {
+      assert.isFalse(RA.overlaps([1, 2], []));
+      assert.isFalse(RA.overlaps([{}], [[]]));
+      assert.isFalse(RA.overlaps([{}], [{ key: 'value' }]));
+      assert.isFalse(RA.overlaps([{ key: 'value' }], [{ key: 'value2' }]));
+      assert.isFalse(RA.overlaps(['-v', '--verbose'], []));
+      assert.isFalse(RA.overlaps([() => 'hi'], [() => 'hi']));
+    });
+  });
+
+  context('given two empty lists', function () {
+    specify('should return true', function () {
+      assert.isTrue(RA.overlaps([], []));
+    });
+  });
+
+  context('given empty list as first argument', function () {
+    specify('should return true', function () {
+      assert.isTrue(RA.overlaps([], [1]));
+    });
+  });
+
+  it('should curry', function () {
+    assert.isTrue(RA.overlaps([1], [1]));
+    assert.isTrue(RA.overlaps([1])([1]));
+  });
+});
diff --git a/types/test/overlaps.ts b/types/test/overlaps.ts
new file mode 100644
index 0000000000..088e36eb3c
--- /dev/null
+++ b/types/test/overlaps.ts
@@ -0,0 +1,16 @@
+import * as RA from 'ramda-adjunct';
+
+RA.overlaps(['-'], ['a']); // $ExpectType boolean
+RA.overlaps([2], [1]); // $ExpectType boolean
+RA.overlaps([2], [1, 2]); // $ExpectType boolean
+RA.overlaps([2, ''], [1, 2]); // $ExpectType boolean
+RA.overlaps([2, null], [1, 2]); // $ExpectType boolean
+RA.overlaps([2])([1, 2]); // $ExpectType boolean
+
+RA.overlaps(1, 2); // $ExpectError
+RA.overlaps('#', '10'); // $ExpectError
+RA.overlaps(['#'], '10'); // $ExpectError
+RA.overlaps([undefined], undefined); // $ExpectError
+RA.overlaps(undefined, [undefined]); // $ExpectError
+RA.overlaps(null, [undefined]); // $ExpectError
+RA.overlaps(() => {}, [undefined]); // $ExpectError