Wisdom
  • Welcome
  • core
    • Flyway
    • Bean Validation
    • Lombok
    • Webclient
      • Generic Webclient Api
      • SSL Certificate
    • Application Event Publisher
    • REST API's Design
      • Http Methods and Status Codes
      • Resource Naming and URL Structure
      • Request / Response Design
      • Versioning Strategies
      • Filtering and Searching In API
    • Spring Boot Mail Integration
      • Sendgrid
    • Template Engines
      • Java Template Engine [JTE]
  • security
    • Complete Guide to URL Matchers in Spring Security: Types, Examples, Pros, Cons, and Best Use Cases
    • Passwordless Login With Spring Security 6
      • Spring Security OneTimeToken
        • One Time Token with default configuration
        • One Time Token with custom configuration
        • One Time Token With Jwt
  • others
    • How to Integrate WhatsApp for Sending Messages in Your Application
  • java
    • Interview Questions
      • Constructor
      • Serialization
      • Abstract Class
    • GSON
      • Type Token
      • Joda Datetime Custom Serializer and Deserializer
  • Nginx
    • Core Concepts and Basics
    • Deep Dive on NGINX Configuration Blocks
    • Deep Dive on NGINX Directives
    • Deep Dive into Nginx Variables
    • Nginx as a Reverse Proxy and Load Balancer
    • Security Hardening in NGINX
    • Performance Optimization & Tuning in NGINX
    • Dynamic DNS Resolution in Nginx
    • Advanced Configuration & Use Cases in NGINX
    • Streaming & Media Delivery in NGINX
    • Final Configuration
  • Angular
    • How to Open a PDF or an Image in Angular Without Dependencies
    • Displaying Colored Logs with Search Highlighting in Angular 6
    • Implementing Auto-Suggestion in Input Field in Angular Template-Driven Forms
    • Creating an Angular Project Using npx Without Installing It Globally
    • Skip SCSS and Test Files in Angular with ng generate
  • Javascript
    • When JavaScript's Set Falls Short for Ensuring Uniqueness in Arrays of Objects
    • Demonstrating a Function to Get the Last N Months in JavaScript
    • How to Convert Numbers to Words in the Indian Numbering System Using JavaScript
    • Sorting Based on Multiple Criteria
  • TYPESCRIPT
    • Using Omit in TypeScript
Powered by GitBook
On this page
  1. Javascript

When JavaScript's Set Falls Short for Ensuring Uniqueness in Arrays of Objects

The Set object in JavaScript is widely regarded as a convenient way to eliminate duplicates from arrays. However, while it works perfectly for primitive values like strings and numbers, it falls short when dealing with arrays of objects. This is because Set determines uniqueness using reference equality, not deep equality. In this article, we’ll explore the limitations of Set and various ways to ensure uniqueness in arrays of objects without relying on external libraries.

Understanding Set Behavior

The Set object ensures that its values are unique by using the SameValueZero algorithm. This works well for primitive values:

const numbers = [1, 2, 3, 2, 1];
const uniqueNumbers = [...new Set(numbers)];

console.log(uniqueNumbers); // Output: [1, 2, 3]

However, with objects, Set checks for reference equality, not deep equality. This means two objects with identical properties but different references are considered unique:

const objects = [
  { id: 1, name: "Alice" },
  { id: 2, name: "Bob" },
  { id: 1, name: "Alice" }
];
const uniqueObjects = [...new Set(objects)];

console.log(uniqueObjects);
// Output: [{ id: 1, name: "Alice" }, { id: 2, name: "Bob" }, { id: 1, name: "Alice" }]

As you can see, Set does not remove the duplicate { id: 1, name: "Alice" }, because the two objects are stored in separate memory locations.

Techniques for Ensuring Uniqueness

1. Using Array.prototype.filter

The filter method allows you to iterate through the array and keep only the objects that meet a specific condition. You can use findIndex to check for duplicates:

const objects = [
  { id: 1, name: "Alice" },
  { id: 2, name: "Bob" },
  { id: 1, name: "Alice" }
];

const uniqueObjects = objects.filter((obj, index, self) =>
  index === self.findIndex(o => o.id === obj.id)
);

console.log(uniqueObjects);
// Output: [{ id: 1, name: "Alice" }, { id: 2, name: "Bob" }]

This approach works well when the uniqueness is based on a single property, like id.


2. Using a Map

A Map allows you to store key-value pairs, where the key can be the unique property of the object. You can use it to track duplicates efficiently:

const objects = [
  { id: 1, name: "Alice" },
  { id: 2, name: "Bob" },
  { id: 1, name: "Alice" }
];

const map = new Map();
objects.forEach(obj => {
  if (!map.has(obj.id)) {
    map.set(obj.id, obj);
  }
});

const uniqueObjects = Array.from(map.values());

console.log(uniqueObjects);
// Output: [{ id: 1, name: "Alice" }, { id: 2, name: "Bob" }]

This method is efficient and scales well for larger datasets.


3. Using reduce

The reduce method provides a concise way to build a new array while keeping track of the seen objects:

const objects = [
  { id: 1, name: "Alice" },
  { id: 2, name: "Bob" },
  { id: 1, name: "Alice" }
];

const uniqueObjects = objects.reduce((acc, obj) => {
  if (!acc.some(item => item.id === obj.id)) {
    acc.push(obj);
  }
  return acc;
}, []);

console.log(uniqueObjects);
// Output: [{ id: 1, name: "Alice" }, { id: 2, name: "Bob" }]

This approach allows for flexibility in defining the uniqueness logic.


4. Using JSON.stringify for Structural Comparison

If the objects are simple and do not involve circular references, you can use JSON.stringify to compare their structures:

const objects = [
  { id: 1, name: "Alice" },
  { id: 2, name: "Bob" },
  { id: 1, name: "Alice" }
];

const uniqueObjects = Array.from(
  new Set(objects.map(obj => JSON.stringify(obj)))
).map(str => JSON.parse(str));

console.log(uniqueObjects);
// Output: [{ id: 1, name: "Alice" }, { id: 2, name: "Bob" }]

This method works when the entire object structure defines uniqueness, but it may not handle complex types like Date or undefined properly.


5. Using a Plain Object as a Dictionary

You can use a plain object to track seen keys for quick lookups:

const objects = [
  { id: 1, name: "Alice" },
  { id: 2, name: "Bob" },
  { id: 1, name: "Alice" }
];

const seen = {};
const uniqueObjects = objects.filter(obj => {
  if (!seen[obj.id]) {
    seen[obj.id] = true;
    return true;
  }
  return false;
});

console.log(uniqueObjects);
// Output: [{ id: 1, name: "Alice" }, { id: 2, name: "Bob" }]

This method is efficient but assumes that the key used for uniqueness (e.g., id) is always present and consistent.


Choosing the Right Method

When deciding which method to use, consider:

  1. The Definition of Uniqueness: Is it based on a single property, multiple properties, or the entire object structure?

  2. Performance Needs: For larger datasets, methods using Map or plain objects are typically faster.

  3. Object Complexity: If objects have nested or complex properties, avoid methods like JSON.stringify.


Conclusion

While the Set object is great for ensuring uniqueness in arrays of primitives, arrays of objects require custom solutions. By leveraging JavaScript's native methods like filter, reduce, and Map, you can effectively deduplicate arrays of objects without relying on external libraries.

PreviousSkip SCSS and Test Files in Angular with ng generateNextDemonstrating a Function to Get the Last N Months in JavaScript

Last updated 4 months ago