Historical Context & Motivation
Every app you use daily—from social media feeds to music playlists—works by taking large collections of data and reshaping them for display. A photo app might shrink all your images to thumbnails; a search engine filters millions of results to show only the relevant ones. At the core of these operations is a simple idea: loop through a list, and build a new list that contains only the items (or transformed versions of items) you actually need.
This pattern is one of the oldest and most universal ideas in programming. Long before JavaScript existed, programmers in languages like FORTRAN and LISP were iterating over arrays and constructing new ones. The pattern has survived because it solves a problem that never goes away: raw data rarely looks the way we want it to, so we must transform values into a useful form and filter out the noise.
The central question this lesson addresses is straightforward: given an existing array, how do you use a loop to produce a brand-new array that contains either modified versions of the original elements (transformation) or only those elements that meet a specific condition (filtering)? Mastering this loop-based pattern gives you the foundation for every data-processing task you will encounter.
Core Principles & Definitions
Before diving into code, let's nail down the vocabulary you'll use throughout this lesson and beyond. Two operations sit at the heart of array processing: transforming (changing every element in some way) and filtering (keeping only the elements that pass a test). Both operations share a common structure: start with an empty result array, loop through the source, and push items into the result.
Source Array
Result Array
push(). When the loop ends, this array holds your output.Transform Pattern
Filter Pattern
if statement, and push only the elements that pass the test.Immutability Mindset
Visual Explanation — The Loop-Based Build Pattern
The following diagram shows how the transform pattern works step by step. The source array [2, 5, 8] is processed by a for loop that doubles each value and pushes it into a new result array. Follow the numbered steps from left to right to see how the result array grows with each iteration.
[4, 10, 16] is complete, while the source [2, 5, 8] remains untouched.Notice the three-part structure that stays the same every time you use this pattern. First, you declare an empty result array before the loop. Second, inside the loop body you compute a new value (or check a condition). Third, you push() the new value into the result. This three-step rhythm is the backbone of both transforming and filtering.
How It Works — Code Patterns in Detail
The Transform Pattern
The transform pattern visits every element in the source array, applies some operation, and pushes the modified value into the result. Here is the general template:
source — the original array. transform() — any expression that converts the current element to its new form. result — the new array that accumulates transformed values.The Filter Pattern
The filter pattern also visits every element, but instead of transforming it, the loop checks a boolean condition. Only elements that satisfy the condition are pushed into the result.
condition() — a boolean expression (e.g., source[i] > 10). Only elements where this is true make it into the result.Combining Transform + Filter
You can combine both patterns in a single loop. For example, you might want to double only the even numbers in an array. In that case, you place the if check first, and inside the if block you transform and push. This shows the real power of understanding the loop-based approach: you can mix and match however your problem demands.
% 2 === 0) and doubles them in one pass. The result contains only the doubled even values.Detailed Breakdown — Transform vs. Filter Side by Side
The diagram below places the transform and filter patterns side by side so you can see exactly where they differ. Both start with the same source array [3, 7, 12, 4, 18]. The left branch transforms every element (adds 10), while the right branch filters for elements greater than 5. Pay attention to the result array sizes—transforms always produce the same number of elements, while filters may produce fewer.
| Feature | Transform | Filter |
|---|---|---|
| Purpose | Change every element | Keep only elements that pass a test |
| Result length | Same as source | ≤ source length |
| Uses if statement? | No (every element is pushed) | Yes (only matching elements) |
| Element values | Different from source | Identical to source |
Worked Example — Building a Filtered & Transformed Array
Suppose you have an array of student test scores and you need to produce a new array that contains only the passing scores (70 or above), each converted to a letter grade. This combines both filtering and transforming in a single loop.
const scores = [88, 42, 95, 67, 73, 58, 100]; This is our source—we will not change it.const passingGrades = []; This starts empty and will grow inside the loop.for (let i = 0; i < scores.length; i++) { if (scores[i] >= 70) { let grade; if (scores[i] >= 90) { grade = 'A'; } else if (scores[i] >= 80) { grade = 'B'; } else { grade = 'C'; } passingGrades.push(grade); } }passingGrades contains the final result.["B", "A", "C", "A"] — four passing grades from seven original scores. The source array scores is unchanged.const result = []; inside the loop, the array resets to empty on every iteration and you'll end up with only the last item!Strengths, Limitations & When to Use Each Pattern
The loop-based build pattern is extremely flexible, but like any tool it comes with trade-offs. Understanding these will help you decide when to use a manual loop and when a built-in method might be more appropriate.
| Aspect | Strength | Limitation |
|---|---|---|
| Flexibility | You can combine filter + transform + any custom logic in a single loop | More lines of code than built-in methods like .map() or .filter() |
| Readability | Easy to trace step by step; great for learning | Can become lengthy for simple operations |
| Performance | Slightly faster than chaining multiple built-in methods | Performance difference is negligible for most high school projects |
| Safety | Builds a new array—original data stays intact | If you accidentally modify source[i] (an object), the original mutates too |
| Debugging | You can add console.log inside the loop to inspect each step | Off-by-one errors (wrong loop bounds) are a common pitfall |
.map() and .filter() are like an automatic transmission—convenient, but they only do what they're designed for. Learning the manual approach first ensures you truly understand what's happening under the hood.Connection to Built-In Methods & Advanced Patterns
Once you're confident with loop-based patterns, you'll be ready to use JavaScript's higher-order array methods. These methods do the same work as the patterns you've learned, but they express it more concisely. The table below shows the direct relationship between each loop pattern and its built-in counterpart.
| Loop-Based Pattern | Built-In Method | What It Does |
|---|---|---|
| Transform with for loop | .map(callback) | Returns a new array with each element transformed by the callback |
| Filter with for loop + if | .filter(callback) | Returns a new array with only elements where callback returns true |
| Accumulate to single value | .reduce(callback, init) | Reduces the array to a single value (sum, count, object, etc.) |
| Check every element | .every(callback) | Returns true only if every element passes the test |
When you eventually write const doubled = nums.map(n => n * 2);, you'll understand that JavaScript is running a loop behind the scenes, creating an empty array, pushing transformed values, and returning the result—exactly the pattern you've practiced in this lesson. That deep understanding is what separates a programmer who uses tools from one who truly understands them.
Practice Problems
const result = []; inside the loop body?const temps = [32, 68, 95, 50, 77]; (Fahrenheit), write a loop that builds a new array called celsius containing each temperature converted to Celsius. Use the formula C = (F − 32) × 5 / 9, rounded to one decimal place.const words = ["apple", "Banana", "cherry", "Date", "elderberry"];, write a loop that builds a new array containing only the words that start with a lowercase letter, and converts each of those words to uppercase.const products = [{name: "Notebook", price: 3.50}, {name: "Pen", price: 1.25}, {name: "Backpack", price: 45.00}, {name: "Eraser", price: 0.75}, {name: "Calculator", price: 12.99}]; Write a loop that builds a new array of strings for items under $5.00. Each string should read like: "Notebook - $3.50".const nums = [4, 9, 12, 7, 6]; const evens = []; for (let i = 1; i <= nums.length; i++) { if (nums[i] % 2 === 0) { evens.push(nums[i]); } }Lesson Summary
In this lesson you learned the fundamental loop-based build pattern for creating new arrays from existing ones. The pattern has three consistent steps: declare an empty result array before the loop, iterate through the source array with a for loop, and push values into the result. When you change each element before pushing, you are transforming; when you use an if statement to decide whether to push, you are filtering.
Key ideas to remember: transforms always produce a result array of the same length as the source, while filters may produce a shorter array. You can combine both patterns in a single loop for more complex tasks. The source array is never modified, keeping your data safe. This loop-based approach is the foundation for understanding JavaScript's built-in methods like .map() and .filter(), which you'll learn next.