It comes down to computation, finding on graph is over the entire dataset whereas in underscore it’s object comparison. The way this is done factors heavily on the speed. Im surprised a purely vanilla method isn’t faster becuase underscore is a library so to make this even faster I would look at doing it in pure JS.
In the past I was able to achieve a huge speed increase in a similar manner by using the .includes method for array lookups in JS - its much faster then anything else out there and I was told that is due to the compilation of JS itself and these methods are worked in to be as quick to compile as possible.
Be mindful of how you benchmark this. The measurements are really only useful if done in a production environment. The JIT compiler can optimize things in ways that aren’t obvious, so it’s entirely possible that that the native version is in fact more performant at load.
This talk discusses the woes of benchmarking. It’s somewhat old, but I imagine the JIT optimizations have only improved since then.
It’s a bit more difficult to do benchmarking on the production system as I can’t run this function 100x or even more just to see a proper time difference. Unless I maybe run the benchmark in a delayed job after the startup of the server is finished but then I’d need to build up the array of objects myself which isn’t as realistic as using real data.