Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JavaScript map & find at the same time: findMap?

How would you rewrite this without using a for loop?

const a = [2, 5, 78, 4];
const expensiveFunction = n => 2 * n;

let result;

// Find the first number 
for (let i = 0; i < a.length; i++) {
    const r = expensiveFunction(a[i]);

    if (r > 100) {
        result = r;
        break;
    }
}

console.log(result);

My naive approach:

const result = a.map(expensiveFunction).find(x => x > 100);
console.log(result);

But this runs expensiveFunction on all the elements, which I would like to avoid. In the above case, we should avoid running expensiveFunction(4).

Some languages have find_map (e.g, Rust), I didn't find it in lodash nor in underscore.

like image 866
jeanpaul62 Avatar asked Sep 02 '19 16:09

jeanpaul62


People also ask

What is a JavaScript map?

A Map holds key-value pairs where the keys can be any datatype. A Map remembers the original insertion order of the keys. A Map has a property that represents the size of the map.

Are maps used in JavaScript?

The map accepts any key type As presented above, if the object's key is not a string or symbol, JavaScript implicitly transforms it into a string. Contrary, the map accepts keys of any type: strings, numbers, boolean, symbols. Moreover, the map preserves the key type. That's the map's main benefit.

What is map in ES6?

ES6 provides us a new collection type called Map, which holds the key-value pairs in which values of any type can be used as either keys or values. A Map object always remembers the actual insertion order of the keys. Keys and values in a Map object may be primitive or objects. It returns the new or empty Map.

Can I use map in object JavaScript?

Map is a collection of elements where each element is stored as a Key, value pair. Map object can hold both objects and primitive values as either key or value. When we iterate over the map object it returns the key, value pair in the same order as inserted.


2 Answers

Built-in map is greedy so you have to write your own, lazy version:

const a = [2, 5, 78, 4];
const expensiveFunction = n => {
     console.log('expensiveFunction for', n); 
     return 2 * n 
};


function *map(a, fn) {
    for(let x of a)
        yield fn(x);
}

function find(a, fn) {
    for(let x of a)
        if (fn(x))
            return x;
}



r = find(map(a, expensiveFunction), x => x > 100)
console.log('result', r)

Unlike the stock map, this map is a generator and returns (yields) results on demand rather than processing the whole array at once. find and map in this example are "coroutines" and play some kind of a ping-pong game where find asks for results and map delivers them when asked. As soon as find is satisfied with what it's got, it quits and so does map, because nobody is asking for its results anymore.

You can also add map, find and friends to the IteratorPrototype to make them available for all iterators and be able to use dot notation:

const IteratorPrototype = Object.getPrototypeOf(Object.getPrototypeOf([][Symbol.iterator]()));

Object.defineProperties(IteratorPrototype, {
    map: {
        value: function* (fn) {
            for (let x of this) {
                yield fn(x);
            }
        },
        enumerable: false
    },

    find: {
        value: function (fn) {
            for (let x of this) {
                if (fn(x))
                    return x;
            }
        },
        enumerable: false
    },

});

//

const a = [2, 5, 78, 4];
const expensiveFunction = n => {
    console.log('expensiveFunction', n);
    return 2 * n
};


let r = a.values().map(expensiveFunction).find(x => x > 100);

console.log(r)

Here's a small library based on this technique: https://github.com/gebrkn/armita

like image 122
5 revs Avatar answered Oct 21 '22 00:10

5 revs


Something like this

const a = [2, 5, 78, 4];
const expensiveFunction = n => 2 * n;
let findMap = arr => {
  let found = arr.find(a => expensiveFunction(a) > 100)
  return found !== undefined ? expensiveFunction(found) : found
}

console.log(findMap(a));

Alert:- JUST out of curiosity , But hacky or you can call it misuse of find

const a = [2, 5, 78, 4];
const expensiveFunction = n => 2 * n;
let findMap = arr => {
  let returnValue;
  let found = arr.find(a => {
    returnValue = expensiveFunction(a)
    return returnValue > 100
  })
  return returnValue
}

console.log(findMap(a));
like image 4
Code Maniac Avatar answered Oct 21 '22 00:10

Code Maniac