DEV Community

Cover image for Creating a Merge Sort Array Prototype for Nested Objects, Strings, and Numbers in JavaScript
Sanu Khan
Sanu Khan

Posted on

Creating a Merge Sort Array Prototype for Nested Objects, Strings, and Numbers in JavaScript

Learn how to extend JavaScript's Array prototype with a powerful and flexible merge sort function that supports nested objects, strings, and numbers.

🧠 Creating a Merge Sort Array Prototype for Nested Objects, Strings, and Numbers

Sorting arrays is easy — until you need to sort deeply nested objects, ensure stable results, or avoid mutating the original array.

In this post, you’ll learn how to create a powerful, non-mutating mergeSortBy() function on the Array.prototype that supports:

  • ✅ Numbers
  • ✅ Strings (case-insensitive)
  • ✅ Complex nested object keys (e.g., "user.address.city")
  • ✅ Custom comparator functions


🔍 Why Not Just Use .sort()?

Feature Array.sort() mergeSortBy() (Custom)
Mutates original array? ✅ Yes ❌ No
Stable sort? ❌ Not guaranteed before ES10 ✅ Always
Deep key sorting? ❌ No ✅ Yes
Supports custom comparator? ✅ Yes ✅ Yes

🔄 Step 1: Get Nested Values

function getValue(obj, path) {
  return path.split('.').reduce((acc, key) => acc?.[key], obj);
}
Enter fullscreen mode Exit fullscreen mode

🧮 Step 2: Comparator Helper

function defaultComparator(a, b) {
  if (a == null && b != null) return -1;
  if (a != null && b == null) return 1;
  if (typeof a === 'string' && typeof b === 'string') {
    return a.localeCompare(b);
  }
  return a > b ? 1 : a < b ? -1 : 0;
}
Enter fullscreen mode Exit fullscreen mode

🧩 Step 3: mergeSortBy on Array.prototype

if (!Array.prototype.mergeSortBy) {
  Array.prototype.mergeSortBy = function (keyOrFn) {
    const getComparator = () => {
      if (typeof keyOrFn === 'function') return keyOrFn;
      if (typeof keyOrFn === 'string') {
        return (a, b) =>
          defaultComparator(getValue(a, keyOrFn), getValue(b, keyOrFn));
      }
      return defaultComparator;
    };

    const merge = (left, right, comparator) => {
      const result = [];
      let i = 0, j = 0;
      while (i < left.length && j < right.length) {
        result.push(
          comparator(left[i], right[j]) <= 0 ? left[i++] : right[j++]
        );
      }
      return result.concat(left.slice(i), right.slice(j));
    };

    const mergeSort = (arr, comparator) => {
      if (arr.length <= 1) return arr;
      const mid = Math.floor(arr.length / 2);
      const left = mergeSort(arr.slice(0, mid), comparator);
      const right = mergeSort(arr.slice(mid), comparator);
      return merge(left, right, comparator);
    };

    return mergeSort(this.slice(), getComparator()); // non-mutating
  };
}
Enter fullscreen mode Exit fullscreen mode

🧪 Test Cases & Real Examples

✅ 1. Sort numbers

[9, 1, 4, 7].mergeSortBy(); // [1, 4, 7, 9]
Enter fullscreen mode Exit fullscreen mode

✅ 2. Sort strings

["banana", "Apple", "cherry"].mergeSortBy();
// Output: ['Apple', 'banana', 'cherry']
Enter fullscreen mode Exit fullscreen mode

✅ 3. Sort objects with deep keys

const users = [
  { id: 1, profile: { name: "John" } },
  { id: 2, profile: { name: "Alice" } },
  { id: 3, profile: { name: "Bob" } },
];

const sortedUsers = users.mergeSortBy("profile.name");
console.log(sortedUsers.map(u => u.profile.name)); 
// ['Alice', 'Bob', 'John']
Enter fullscreen mode Exit fullscreen mode

✅ 4. Custom comparator

const scores = [
  { name: "A", score: 90 },
  { name: "B", score: 100 },
  { name: "C", score: 95 },
];

const desc = scores.mergeSortBy((a, b) => b.score - a.score);
console.log(desc.map(x => x.score)); // [100, 95, 90]
Enter fullscreen mode Exit fullscreen mode

⏱ Performance Comparison: .sort() vs mergeSortBy()

const bigArray = Array.from({ length: 100000 }, () => ({
  id: Math.random().toString(36).substring(7),
  value: Math.floor(Math.random() * 100)
}));

console.time("Native sort");
bigArray.slice().sort((a, b) => a.value - b.value);
console.timeEnd("Native sort");

console.time("mergeSortBy");
bigArray.mergeSortBy("value");
console.timeEnd("mergeSortBy");
Enter fullscreen mode Exit fullscreen mode

✅ Stability Test

const people = [
  { name: "Alice", age: 30 },
  { name: "Bob", age: 25 },
  { name: "Charlie", age: 30 },
];

const sorted = people.mergeSortBy("age");
console.log(sorted.map(p => p.name)); 
// ['Bob', 'Alice', 'Charlie'] — Alice before Charlie (same age) ⇒ stable!
Enter fullscreen mode Exit fullscreen mode

🎯 Final Takeaways

You just built a custom merge sort that:

  • Handles primitives and nested objects
  • Accepts string paths or comparator functions
  • Is stable and predictable

⚠️ Performance: Why Native .sort() Is Faster Than mergeSort()

After testing both Array.prototype.sort() and our custom mergeSort() on complex nested data, the results are clear:

Native Sort: ✅ Faster (~1.3ms)  
Merge Sort: ❌ Slower (~8.5ms)
Enter fullscreen mode Exit fullscreen mode

🚀 Why is Native .sort() Faster?

Reason Description
Engine Optimized Native .sort() is implemented in low-level languages like C++ inside JS engines (e.g., V8).
Hybrid Sorting Modern engines use Timsort — a hybrid of Merge and Insertion sort — optimized for real-world data.
Better Memory Handling Avoids frequent array cloning operations like slice() and shift(), which are costly in JavaScript.
In-place Sorting Operates directly on the original array without creating new intermediate arrays.
Small-Array Optimization For short arrays, engines switch to insertion sort, which is extremely fast.

📚 Then Why Bother With mergeSort()?

While native sort is faster, writing your own mergeSort() still has value:

  • Educational: Great way to learn recursion and sorting logic.
  • 🔍 Transparent: Full control over how elements are compared and sorted.
  • 🔄 Immutable Structures: Useful in environments where mutation is discouraged.
  • 📊 Benchmarking: Compare against other algorithms (QuickSort, HeapSort, etc.) for algorithm study.

🧠 Takeaway

Use .sort() in production.

Use mergeSort() to understand how sorting works under the hood.



📬 What Next?

Let me know:

  • Would you like a TypeScript version?
  • Should this be a standalone NPM utility?

⭐ Found this helpful? Drop a like, comment, or bookmark!


Top comments (0)