diff options
Diffstat (limited to 'js/where/where.js')
-rw-r--r-- | js/where/where.js | 120 |
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; +} |