skip to Main Content

Let’s say we have a function that makes expensive calculation and an array of source values for which we do the calculation. We want to get a first return value of the function that is not null and return it.

function expensiveFunction(params) {...}

// this is how it can be achieved using ordinary for loop
let paramsCollection = ["params1", "params2" /*, ...*/];
let result;
for (let i=0; i<paramsCollection.length; i++) {
    result = expensiveFunction(paramsCollection[i]);
    if (result) {
        break;
    }
}

I’d like to do it using Array methods.

Array.find will only return the element for which function returns something.

Array.map will run expensive calculations for all elements even if first one was ok.

Array.reduce can do the job, but it will iterate over an array anyway – even if it doesn’t call the expensiveFunction it feels bad..

let paramsCollection = ["params1", "params2" /*, ...*/];
let result = paramsCollection.reduce((acc, cur) => acc ? acc : expensiveFunction(cur), null);

Any idea how to make it this way but w/out drawbacks from above examples?

3

Answers


  1. You can write your own mapFind based on Array.prototype.find:

    function mapFind(arr, f) {
      let result;
      
      arr.find(el => {
        let temp = f(el);
        if (temp) result = temp;
        return temp;
      });
      return result;
    }
    
    function expensiveFunction(params) {
      return params > 7 ? params * params : 0;
    }
    console.log(mapFind([1, 2, 3], expensiveFunction));
    console.log(mapFind([7, 8, 9], expensiveFunction));

    You have a contradiction in your question: if (result) and "not null" are not equivalent. I assumend if (result). If you actually mean "not null", you can replace if (temp) with if (temp !== null) and return temp; with return temp !== null;.

    Login or Signup to reply.
  2. ECMAScript 2025 will include iterator helpers, which are being rolled out now, so if you have that available (like in current versions of Edge, Chrome, …), then this would do it:

    paramsCollection.values().map(expensiveFunction).find(Boolean);
    

    Note that:

    • .values() returns an iterator (not an array).
    • .map() and .find() are not array methods, but (new) iterator methods.
    • As the mapping of an item happens before the find on that item, we get a result produced by expensiveFunction.
    • Contrary to how array methods work, the iterator’s map method will only consume values as they are needed by the iterator’s find method. Once the find method has found the value that is truthy, map will not consume any more values.

    NB: If you only want to skip null values (but not other falsy values), then the .find call back could be x => x !== null.

    Example:

    function expensiveFunction(params) {
        return params == "params2" ? "hello" : "";
    }
    
    // Example
    const paramsCollection = ["params1", "params2", "params3"];
    const res = paramsCollection.values().map(expensiveFunction).find(Boolean);
    console.log(res);
    Login or Signup to reply.
  3. I’m sure I’ve missed something in the nuance of the question, but why can’t you just use findIndex:

    function expensiveFunction(param) { return param == "params2"}
    
    let paramsCollection = ["params1", "params2", "params3"];
    
    let index = paramsCollection.findIndex(expensiveFunction);
    
    if(index){
      console.log(paramsCollection[index]);
    }

    findIndex runs a function, returning the first index that returns a non-falsey value.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search