Lodash omitBy vs Object.entries Performance

When processing large-scale JavaScript objects, choosing between Lodash's _.omitBy and native methods using Object.entries significantly affects runtime execution speed and heap memory allocation. While Object.entries benefits from native C++ engine optimizations, combining it with functional array methods creates intermediate data structures that can degrade performance during massive iterations compared to direct iteration patterns like _.omitBy.

How Lodash _.omitBy Operates Under the Hood

Lodash's _.omitBy is designed to construct a shallow copy of an object while excluding properties that satisfy a predicate function.

During iteration over a large object, _.omitBy:

  1. Traverses the object's enumerable own properties using internal iterator methods.
  2. Executes the predicate callback for every key-value pair.
  3. Assigns matching properties directly to a newly allocated result object.

The primary benefit of _.omitBy during massive iterations is memory predictability. It does not construct temporary collections of keys or values prior to filtering; it processes the object linearly and populates the target object in a single pass. However, it incurs the execution overhead of invoking a JavaScript callback for every single property, which prevents V8 from inlining the operation fully across millions of iterations.

The Cost of the Object.entries Native Pipeline

A typical native equivalent to _.omitBy combines Object.entries, Array.prototype.filter, and Object.fromEntries:

Object.fromEntries(
  Object.entries(sourceObject).filter(([key, value]) => !predicate(value, key))
);

While native methods are highly optimized, this pipeline creates severe bottlenecks when handling objects with hundreds of thousands of keys:

  1. Massive Memory Allocation: Object.entries instantly allocates an array containing N sub-arrays, each holding two elements ([key, value]). On a massive dataset, creating these millions of short-lived arrays triggers heavy memory usage.
  2. Garbage Collection Pressure: After Object.entries runs, Array.prototype.filter creates another array to hold the filtered tuples. Once Object.fromEntries reconstructs the final object, all intermediate arrays become unreachable, forcing immediate and frequent Garbage Collection (GC) pauses.
  3. Multi-Pass Latency: The native pipeline requires three sequential passes over the dataset: one to create the entries array, one to filter the entries, and one to reconstruct the final object.

Performance and Memory Comparison

The Most Performant Alternative

If absolute performance on massive objects is critical, neither _.omitBy nor Object.entries is optimal. A direct for...in loop or a for...of loop over Object.keys() eliminates both intermediate array allocations and per-property callback overhead:

const result = {};
const keys = Object.keys(sourceObject);

for (let i = 0; i < keys.length; i++) {
  const key = keys[i];
  const value = sourceObject[key];
  if (!predicate(value, key)) {
    result[key] = value;
  }
}

This imperative approach is considerably faster than _.omitBy and uses a fraction of the memory consumed by Object.entries.