Hone logo
Hone
Problems

Building a Data Reconciliation Component in React with TypeScript

This challenge focuses on building a core component for data reconciliation. Reconciliation is the process of comparing two datasets to identify discrepancies, additions, and deletions. This is a common task in data management, financial applications, and state synchronization. You will create a React component that visually highlights these differences between two lists of items.

Problem Description

You are tasked with building a React component that takes two arrays of objects (representing two versions of a dataset) and visually highlights the differences between them. The component should identify:

  • Added Items: Items present in the "new" list but not in the "old" list.
  • Deleted Items: Items present in the "old" list but not in the "new" list.
  • Modified Items: Items present in both lists but with at least one differing property value.
  • Unchanged Items: Items present in both lists with identical property values.

The component should render these items with distinct visual cues (e.g., background colors, icons) to clearly indicate their status. You will need a reliable way to identify and compare individual items. For simplicity, assume items are uniquely identified by an id property.

Key Requirements:

  1. Component Structure: Create a functional React component (e.g., DataReconciler).
  2. Props: The component should accept two props:
    • oldData: An array of objects representing the previous state.
    • newData: An array of objects representing the current state.
  3. Item Identification: Items are uniquely identified by an id property (string or number).
  4. Difference Detection: Implement logic to compare oldData and newData and categorize items into the four types mentioned above.
  5. Visual Representation: Render the items from newData, visually indicating their reconciliation status:
    • Added: Distinct style (e.g., green background).
    • Deleted: This will be represented by not appearing in newData but identified through the reconciliation logic. The rendered output should primarily reflect newData. However, your internal reconciliation logic must account for deletions.
    • Modified: Distinct style (e.g., yellow background).
    • Unchanged: Default or neutral style (e.g., white background).
  6. Display of Properties: For modified items, ideally, you should also highlight which specific properties have changed. (This is an advanced but highly desirable feature.)
  7. TypeScript: Use TypeScript for type safety. Define appropriate interfaces for your data items.

Expected Behavior:

When provided with oldData and newData, the component should render a list of items from newData. Each item should have a visual indicator of its status (added, modified, unchanged). If an item was present in oldData but is missing in newData, it should be implicitly considered "deleted" by the reconciliation process, though it won't be rendered unless you decide to explicitly show a list of deleted items as a separate output. For this challenge, focus on rendering the newData with clear status.

Edge Cases:

  • Empty oldData or newData arrays.
  • Both arrays are empty.
  • Items with identical IDs but different property values.
  • Items with different data types for the same property.

Examples

Example 1: Basic Additions and Unchanged

interface Item {
  id: string;
  name: string;
  value: number;
}

const oldData: Item[] = [
  { id: "a", name: "Apple", value: 10 },
  { id: "b", name: "Banana", value: 20 },
];

const newData: Item[] = [
  { id: "a", name: "Apple", value: 10 }, // Unchanged
  { id: "b", name: "Banana", value: 20 }, // Unchanged
  { id: "c", name: "Cherry", value: 30 }, // Added
];

Expected Output (Conceptual - actual rendering will vary):

A list rendering "Apple", "Banana", and "Cherry".

  • "Apple" and "Banana" would have a neutral background.
  • "Cherry" would have a distinct "added" background (e.g., green).

Example 2: Modifications and Deletions

interface Product {
  id: number;
  name: string;
  price: number;
  stock: number;
}

const oldData: Product[] = [
  { id: 101, name: "Laptop", price: 1200, stock: 5 }, // Will be modified
  { id: 102, name: "Keyboard", price: 75, stock: 15 }, // Will be deleted
  { id: 103, name: "Mouse", price: 25, stock: 50 },  // Unchanged
];

const newData: Product[] = [
  { id: 101, name: "Laptop", price: 1150, stock: 3 }, // Modified (price and stock changed)
  { id: 103, name: "Mouse", price: 25, stock: 50 },  // Unchanged
  { id: 104, name: "Monitor", price: 300, stock: 10 }, // Added
];

Expected Output (Conceptual):

A list rendering "Laptop", "Mouse", and "Monitor".

  • "Laptop" would have a distinct "modified" background (e.g., yellow). Ideally, property changes would be highlighted.
  • "Mouse" would have a neutral background.
  • "Monitor" would have a distinct "added" background (e.g., green).
  • The "Keyboard" would be identified internally as deleted but not rendered.

Example 3: Empty Arrays

interface SimpleItem {
  id: string;
}

const oldData: SimpleItem[] = [];
const newData: SimpleItem[] = [{ id: "x" }];

Expected Output (Conceptual):

A list rendering "x".

  • "x" would have an "added" background.

Constraints

  • Input arrays can contain up to 1000 items each.
  • The id property will be of type string or number.
  • Object properties to compare will be primitive types (string, number, boolean). Deep comparison of nested objects is out of scope.
  • The reconciliation logic should be reasonably performant. An O(N*M) approach might be acceptable for moderate datasets, but O(N log N) or O(N) (where N and M are the sizes of the input arrays) would be preferred for larger datasets.
  • The component should render using React.

Notes

  • Consider using a Map or an object for efficient lookup of items by id when performing the reconciliation.
  • For modified items, you might want to return an object that details which properties changed. This can be achieved by iterating through the properties of the old and new items and comparing their values.
  • Think about how you will handle the "deleted" items. While not explicitly rendered in the primary list, your reconciliation logic must identify them. You could optionally add a separate display area for deleted items.
  • For the visual styling, use simple CSS classes or inline styles to indicate the status.
Loading editor...
typescript