Repository: char0n/ramda-adjunct
Pull request #1479 (2020-05-30 00:02:36)

feat: findOr
https://github.com/char0n/ramda-adjunct/issues/372

Probably messed something up, please let me know!

# Pull request template

Please use the following [PR](https://github.com/char0n/ramda-adjunct/pull/905/files) as an example
of pull request. This PR deals with adding a new feature called `allSettledP`.

If you have any additional questions please don't hesitate to contact any of the core committers.


char0n: @AHB are you willing to continue on this PR and finish it?
char0n: This PR should be ready for merging on master.

Patch:
diff --git a/src/findOr.js b/src/findOr.js
new file mode 100644
index 0000000000..4a6862a03a
--- /dev/null
+++ b/src/findOr.js
@@ -0,0 +1,30 @@
+import { pipe, curry, find, defaultTo } from 'ramda';
+
+/**
+ * Returns the first element of the list which matches the predicate.
+ * Returns default value if no element matches or matched element is `null`, `undefined` or `NaN`.
+ * Dispatches to the find method of the second argument, if present.
+ * Acts as a transducer if a transformer is given in list position.
+ *
+ * @func findOr
+ * @memberOf RA
+ * @since {@link https://char0n.github.io/ramda-adjunct/2.32.0|v2.32.0}
+ * @category List
+ * @sig  a -> (b -> Boolean) -> [b] -> b | a
+ * @param {*} defaultValue The default value
+ * @param {Function} fn The predicate function used to determine if the element is the desired one.
+ * @param {Array} list The array to consider.
+ * @return {*} The element found, or the default value.
+ * @see {@link http://ramdajs.com/docs/#defaultTo|R.defaultTo}, {@link http://ramdajs.com/docs/#find|R.find}
+ * @example
+ *
+ * RA.findOr(1, isUndefined, [1, 2, undefined]); // => 1
+ * RA.findOr(1, val => val === 2, [1, 2, undefined]); // => 2
+ * RA.findOr(1, val => val === 3, [1, 2, undefined]); // => 1
+ */
+
+const findOr = curry((defaultVal, fn, list) =>
+  pipe(find(fn), defaultTo(defaultVal))(list)
+);
+
+export default findOr;
diff --git a/src/index.js b/src/index.js
index e5fab9450c..254e065558 100644
--- a/src/index.js
+++ b/src/index.js
@@ -159,6 +159,7 @@ export { default as notAllUnique } from './notAllUnique';
 export { default as sortByProps } from './sortByProps';
 export { default as skipTake } from './skipTake';
 export { default as rangeStep } from './rangeStep';
+export { default as findOr } from './findOr';
 // Object
 export { default as invoke } from './invoke';
 export { default as invokeArgs } from './invokeArgs';
diff --git a/types/index.d.ts b/types/index.d.ts
index cadce0ef1b..624d474d6e 100644
--- a/types/index.d.ts
+++ b/types/index.d.ts
@@ -1171,6 +1171,19 @@ declare namespace RamdaAdjunct {
         defaultWhen<DefVal, Val>(predicate: Function, defaultVal: DefVal): (val: Val) => DefVal | Val;
         defaultWhen(predicate: Function): <DefVal, Val>(defaultVal: DefVal) => (val: Val) => DefVal | Val;
 
+        /**
+         * Returns the first element of the list which matches the predicate.
+         * Returns default value if no element matches or matched element is `null`, `undefined` or `NaN`.
+         * Dispatches to the find method of the second argument, if present.
+         * Acts as a transducer if a transformer is given in list position.
+         */
+        findOr<DefVal, T>(defaultVal: DefVal, predicate: (element: T) => boolean, list: ReadonlyArray<T>): T | DefVal;
+        findOr<DefVal, T>(defaultVal: DefVal, predicate: (element: T) => boolean): (list: ReadonlyArray<T>) => T | DefVal;
+        findOr<DefVal, T>(defaultVal: DefVal): {
+            (predicate: (element: T) => boolean, list: ReadonlyArray<T>): T | DefVal;
+            (predicate: (element: T) => boolean): (list: ReadonlyArray<T>) => T | DefVal;
+        };
+
         /**
          * Y-combinator
          *


Test patch:
diff --git a/test/findOr.js b/test/findOr.js
new file mode 100644
index 0000000000..9efaa61848
--- /dev/null
+++ b/test/findOr.js
@@ -0,0 +1,79 @@
+import { assert } from 'chai';
+
+import * as RA from '../src';
+
+describe('findOr', function () {
+  context('given a list with a matching value', function () {
+    context('and that value is not Nil nor NaN', function () {
+      specify('should return first matched value', function () {
+        const defaultValue = 'default';
+        const list = [1, 2, 3];
+        const predicate = (val) => val === 1;
+
+        assert.strictEqual(RA.findOr(defaultValue, predicate, list), 1);
+      });
+    });
+
+    context('and that value is NaN', function () {
+      specify('should return the default value', function () {
+        const defaultValue = 'default';
+        const list = [1, 2, 3, NaN];
+        const predicate = (val) => RA.isNaN(val);
+
+        assert.strictEqual(
+          RA.findOr(defaultValue, predicate, list),
+          defaultValue
+        );
+      });
+    });
+
+    context('and that value is null', function () {
+      specify('should return the default value', function () {
+        const defaultValue = 'default';
+        const list = [1, 2, 3, null];
+        const predicate = (val) => val === null;
+
+        assert.strictEqual(
+          RA.findOr(defaultValue, predicate, list),
+          defaultValue
+        );
+      });
+    });
+
+    context('and that value is undefined', function () {
+      specify('should return the default value', function () {
+        const defaultValue = 'default';
+        const list = [1, 2, 3, null];
+        const predicate = (val) => val === undefined;
+
+        assert.strictEqual(
+          RA.findOr(defaultValue, predicate, list),
+          defaultValue
+        );
+      });
+    });
+  });
+
+  context('given a list without a matching value', function () {
+    specify('should return the default value', function () {
+      const defaultValue = 'default';
+      const list = [1, 2, 3];
+      const predicate = (val) => val === 4;
+
+      assert.strictEqual(
+        RA.findOr(defaultValue, predicate, list),
+        defaultValue
+      );
+    });
+  });
+
+  it('should curry', function () {
+    const fn = (val) => val === 4;
+    const list = [1, 2, 3, 4];
+
+    assert.strictEqual(RA.findOr(4, fn, list), 4);
+    assert.strictEqual(RA.findOr(4)(fn, list), 4);
+    assert.strictEqual(RA.findOr(4, fn)(list), 4);
+    assert.strictEqual(RA.findOr(4)(fn)(list), 4);
+  });
+});
diff --git a/types/test/findOr.ts b/types/test/findOr.ts
new file mode 100644
index 0000000000..19a566916c
--- /dev/null
+++ b/types/test/findOr.ts
@@ -0,0 +1,14 @@
+import * as RA from 'ramda-adjunct';
+
+RA.findOr(1, val => val === 1, [1, 2, 3]); // $ExpectType number
+RA.findOr('1', val => val === '1', ['1', '2', '3']); // $ExpectType string
+RA.findOr('1', val => val === 4, [1, 2, 3]); // $ExpectType number | "1"
+RA.findOr(1, () => false, [1, 2, 3]); // $ExpectType number
+
+RA.findOr<'1', number>('1')(val => val === 4, [1, 2, 3]); // $ExpectType number | "1"
+RA.findOr<'1', number>('1', val => val === 4)([1, 2, 3]); // $ExpectType number | "1"
+RA.findOr<'1', number>('1')(val => val === 4)([1, 2, 3]); // $ExpectType number | "1"
+
+RA.findOr(1, val => val, [1, 2, 3]); // $ExpectError
+RA.findOr(1, val => val); // $ExpectError
+RA.findOr(); // $ExpectError