<!DOCTYPE html>
<html lang="en">
<head>
<title>Where?</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
body {
font-family: monospace;
padding: 20px;
max-width: 800px;
margin: 0 auto;
line-height: 1.6;
}
.console {
background: #f5f5f5;
padding: 20px;
border-radius: 4px;
margin-top: 20px;
}
.example {
background: #e9f5ff;
padding: 15px;
border-radius: 4px;
margin: 10px 0;
}
code {
background: #f0f0f0;
padding: 2px 4px;
border-radius: 3px;
}
pre {
background: #f8f8f8;
padding: 15px;
border-radius: 4px;
overflow-x: auto;
}
</style>
</head>
<body>
<h1>Where?</h1>
<section>
<h2>Overview</h2>
<p>
This <code>where()</code> function aims to provide sql-like filtering for arrays of objects.
It allows you to filter collections using simple or complex criteria, including nested properties.
</p>
<p>
Could you usually use <code>Array.filter()</code> instead? Yes, almost always. But I think this is a bit prettier.
</p>
</section>
<section>
<h2>Basic Usage</h2>
<div class="example">
<pre>
const users = [
{ name: 'Tziporah', age: 30 },
{ name: 'Bruce', age: 25 },
{ name: 'Ruth', age: 30 }
];
// Find everyone who is 30 years old
const thirtyYearOlds = where(users, { age: 30 });
// Result: [{ name: 'Tziporah', age: 30 }, { name: 'Ruth', age: 30 }]</pre>
</div>
</section>
<section>
<h2>More Advanced Features</h2>
<h3>1. Nested Object Properties</h3>
<div class="example">
<pre>
const users = [
{
name: 'Bruce',
preferences: {
theme: 'dark',
notifications: { email: true }
}
}
];
// Find people with the dark theme enabled
const darkThemeUsers = where(users, {
preferences: { theme: 'dark' }
});</pre>
</div>
<h3>2. Dot Notation</h3>
<div class="example">
<pre>
// Same query using dot notation
const darkThemeUsers = where(users, {
'preferences.theme': 'dark'
});</pre>
</div>
<h3>3. Multiple Criteria</h3>
<div class="example">
<pre>
// Find folks who are 30 AND prefer dark theme
const result = where(users, {
age: 30,
'preferences.theme': 'dark'
});</pre>
</div>
<h3>4. Array Matching</h3>
<div class="example">
<pre>
const users = [
{
name: 'Jeremiah',
hobbies: ['reading', 'music']
}
];
// Find people with exact hobby matches
const readers = where(users, {
hobbies: ['reading', 'music']
});</pre>
</div>
</section>
<section>
<h2>The Key Features</h2>
<ul>
<li>Deep object comparison</li>
<li>Handles null/undefined values</li>
<li>Supports nested object and dot notation</li>
<li>Exact array matching</li>
<li>Multiple criteria (AND logic)</li>
<li>Memoized for performance 🏃♂️</li>
</ul>
</section>
<section>
<h2>Limitations & Edge Cases</h2>
<h3>1. Partial Array Matches</h3>
<div class="example">
<pre>
const users = [
{
name: 'Bruce',
hobbies: ['reading', 'music', 'hiking']
}
];
// This won't match Bruce's hobbies
const result = where(users, {
hobbies: ['reading', 'music']
}); // Returns []
// Arrays must match exactly in length and content</pre>
</div>
<h3>2. OR Operations</h3>
<div class="example">
<pre>
// This won't work for finding users aged 25 OR 30
const result = where(users, {
age: [25, 30] // This looks for an exact array match
});
// Consider using Array.filter() directly for OR operations:
const result = users.filter(user =>
user.age === 25 || user.age === 30
);</pre>
</div>
<h3>3. Complex Comparisons</h3>
<div class="example">
<pre>
// These operations are NOT supported:
where(users, {
age: (age) => age > 30, // Function predicates
salary: { $gt: 50000 }, // MongoDB-style operators 🤮
'name.length': 4 // Property operations
});</pre>
</div>
<h3>4. Regular Expressions</h3>
<div class="example">
<pre>
// RegExp matching also isn't implemented
where(users, {
email: /.*@gmail\.com$/
});
// Use Array.filter() instead:
const gmailUsers = users.filter(user =>
/.*@gmail\.com$/.test(user.email)
);</pre>
</div>
<h3>5. When Not to Use</h3>
<ul>
<li><strong>Complex Filtering Logic:</strong> If you need OR conditions, ranges, or custom predicates</li>
<li><strong>Large Data Sets:</strong> When performance is critical and you need indexed operations...probably don't use JavaScript</li>
<li><strong>Dynamic Queries:</strong> When query conditions need to be built from different operators on the fly</li>
<li><strong>Partial Matches:</strong> When you need partial array matches or when a string contains operations</li>
</ul>
<h3>6. Performance Considerations</h3>
<div class="example">
<pre>
// Avoid deeply nesting with large arrays
const deeplyNested = where(largeArray, {
'level1.level2.level3.level4.property': value
});
// Each level of nesting increases complexity
// Use direct access whenever possible:
const result = largeArray.filter(item =>
item.level1?.level2?.level3?.level4?.property === value
);</pre>
</div>
</section>
<h2>Test Results</h2>
<div class="console" id="output"></div>
<script>
// Capture console output
const output = document.getElementById('output');
const originalLog = console.log;
const originalError = console.error;
console.log = function(msg) {
output.innerHTML += msg + '<br>';
originalLog.apply(console, arguments);
};
console.error = function(msg) {
output.innerHTML += `<span style="color: red">${msg}</span><br>`;
originalError.apply(console, arguments);
};
</script>
<script src="where.js"></script>
<script src="where.test.js"></script>
</body>
</html>