about summary refs log tree commit diff stats
path: root/js/where/where.js
diff options
context:
space:
mode:
Diffstat (limited to 'js/where/where.js')
-rw-r--r--js/where/where.js120
1 files changed, 120 insertions, 0 deletions
diff --git a/js/where/where.js b/js/where/where.js
new file mode 100644
index 0000000..9ff6545
--- /dev/null
+++ b/js/where/where.js
@@ -0,0 +1,120 @@
+/**
+ * A functional implementation of a query filter similar to SQL's WHERE clause
+ * @param {Array} collection - Array of objects to filter
+ * @param {Object} properties - Object containing properties to match against
+ * @returns {Array} - Filtered array of objects
+ */
+const where = (collection, properties) => {
+    // Handle null/undefined inputs
+    if (!collection || !properties) {
+        return [];
+    }
+
+    // Cache property paths and their split versions for performance
+    const propertyPaths = Object.entries(properties).map(([key, value]) => ({
+        path: key,
+        parts: key.split('.'),
+        value
+    }));
+
+    // Optimized nested value getter with memoization
+    const getNestedValue = (() => {
+        const cache = new WeakMap();
+        
+        return (obj, parts) => {
+            if (!obj) return undefined;
+            
+            let cached = cache.get(obj);
+            if (!cached) {
+                cached = new Map();
+                cache.set(obj, cached);
+            }
+            
+            const pathKey = parts.join('.');
+            if (cached.has(pathKey)) {
+                return cached.get(pathKey);
+            }
+
+            const value = parts.reduce((current, key) => 
+                current && current[key] !== undefined ? current[key] : undefined, 
+                obj
+            );
+            
+            cached.set(pathKey, value);
+            return value;
+        };
+    })();
+
+    // Optimized equality comparison
+    const isEqual = (value1, value2) => {
+        // Handle null/undefined cases first for early return
+        if (value2 === undefined) return true;
+        if (value1 === value2) return true;
+        if (value1 === null || value2 === null) return false;
+
+        // Handle array comparison
+        if (Array.isArray(value1) && Array.isArray(value2)) {
+            return value1.length === value2.length &&
+                   value1.every((val, idx) => isEqual(val, value2[idx]));
+        }
+
+        // Handle object comparison
+        if (typeof value2 === 'object') {
+            return Object.entries(value2).every(([key, val]) => 
+                value1 && isEqual(value1[key], val)
+            );
+        }
+
+        return false;
+    };
+
+    // Optimized property matcher
+    const matchesProperties = item => 
+        propertyPaths.every(({ parts, value }) => 
+            isEqual(getNestedValue(item, parts), value)
+        );
+
+    // Input validation
+    if (!Array.isArray(collection) || typeof properties !== 'object') {
+        return [];
+    }
+
+    return collection.filter(matchesProperties);
+};
+
+// Example usage with nested structures:
+/*
+const data = [
+    { 
+        name: 'John',
+        age: 30,
+        preferences: { 
+            theme: 'dark',
+            notifications: { email: true, sms: false }
+        }
+    },
+    { 
+        name: 'Jane',
+        age: 25,
+        preferences: { 
+            theme: 'light',
+            notifications: { email: false, sms: true }
+        }
+    }
+];
+
+// Find users with specific preferences
+const darkThemeUsers = where(data, { 
+    preferences: { theme: 'dark' } 
+});
+
+// Find users with specific notification settings
+const emailSubscribers = where(data, { 
+    preferences: { notifications: { email: true } } 
+});
+*/
+
+// Export for module usage
+if (typeof module !== 'undefined' && module.exports) {
+    module.exports = where;
+}