Skip to content
Lodash Collections & Arrays — Complete Data Transformation Guide

Lodash Collections & Arrays — Complete Data Transformation Guide

DodaTech Updated Jun 6, 2026 19 min read

Lodash collection and array methods transform, search, group, and filter data in one call, working uniformly on arrays and objects without manual loops.

What You’ll Learn

  • Collection iteration: _.forEach, _.map, _.filter, _.reduce, _.reduceRight
  • Searching: _.find, _.findLast, _.includes, _.some, _.every
  • Grouping and aggregation: _.groupBy, _.keyBy, _.countBy
  • Array operations: _.chunk, _.compact, _.uniq, _.union, _.intersection, _.difference
  • Flattening nested arrays: _.flatten, _.flattenDeep
  • Sorting: _.sortBy, _.orderBy

Why Collections & Arrays Matter

Think of data as items in a filing cabinet. Arrays are like numbered lists (item 1, item 2, item 3) and objects are like labeled folders (name: Alice, role: admin). Lodash lets you work with both using the same syntax and functions.

In Durga Antivirus Pro, file scan results arrive as arrays of threat objects. Before analysis, these arrays must be filtered (remove non-malicious files), sorted (by threat severity), grouped (by file type), and deduplicated (same signature can’t appear twice). Lodash collection methods handle this pipeline in a few lines instead of dozens.

Learning Path

    flowchart LR
  A["Lodash<br/>Getting Started"] --> B["Collections<br/>& Arrays"]
  B --> C["Objects &<br/>Functions"]
  B --> D["Real Project:<br/>Data Pipeline"]
  C --> E["Utilities &<br/>Performance"]
  style B fill:#38bdf8,color:#0f172a,stroke:#38bdf8,stroke-width:2px
  style D fill:#22c55e,color:#0f172a
  
Before this lesson, complete https://tutorials.dodatech.com/frontend/libraries/lodash/lodash-getting-started/ to understand installation, imports, and chaining. You should also be comfortable with basic JavaScript arrays and objects.

Collection Iteration

The key difference between Lodash collection methods and native JavaScript array methods: Lodash works on both arrays and objects. Native Array.prototype.map only works on arrays.

_.forEach — Visit Every Item

Like reading every card in a filing cabinet drawer. Returns the collection so you can chain.

import forEach from 'lodash/forEach';

// Arrays — same as native forEach
forEach([1, 2, 3], (value, index) => {
  console.log(index, value);
});
// 0 1
// 1 2
// 2 3

// Objects — iterates over own properties
forEach({ a: 1, b: 2 }, (value, key) => {
  console.log(key, value);
});
// 'a' 1
// 'b' 2

Why bother with _.forEach instead of for...in? _.forEach only iterates over own enumerable properties — it skips prototype-chain properties that for...in would include. This prevents bugs where inherited methods accidentally show up in your iteration.

_.map — Transform Every Item

Think of _.map as a factory conveyor belt: each item goes in, gets transformed by a function, and comes out the other end as a new item.

import map from 'lodash/map';

// Double every number
map([1, 2, 3], n => n * 2);
// [2, 4, 6]

// Works on objects too!
map({ a: 2, b: 4, c: 6 }, (value, key) => `${key}:${value}`);
// ['a:2', 'b:4', 'c:6']

The property shorthand is one of those small features that saves a ton of typing:

const users = [{ name: 'Alice' }, { name: 'Bob' }, { name: 'Charlie' }];

// Without shorthand:
map(users, user => user.name);
// ['Alice', 'Bob', 'Charlie']

// With shorthand — just pass the property name as a string:
map(users, 'name');
// ['Alice', 'Bob', 'Charlie']

_.filter and _.reject — Keep or Remove Items

_.filter keeps items where the predicate returns truthy. _.reject does the opposite — it keeps items where the predicate returns falsy.

Think of _.filter as a bouncer at a club checking IDs. Only those who pass the check get in.

import filter from 'lodash/filter';
import reject from 'lodash/reject';

const numbers = [1, 2, 3, 4, 5, 6];

filter(numbers, n => n % 2 === 0);
// [2, 4, 6]

reject(numbers, n => n % 2 === 0);
// [1, 3, 5]

The matches shorthand is extremely useful for filtering arrays of objects:

const inventory = [
  { item: 'apple', qty: 10 },
  { item: 'banana', qty: 0 },
  { item: 'orange', qty: 5 },
];

// Find items with qty === 0 — like finding out-of-stock products
filter(inventory, { qty: 0 });
// [{ item: 'banana', qty: 0 }]

_.reduce — Combine Into a Single Value

_.reduce is like folding a stack of papers into one envelope. You start with an accumulator, visit each item, and combine them.

import reduce from 'lodash/reduce';

// Sum all numbers — classic example
reduce([1, 2, 3], (sum, n) => sum + n, 0);
// 6

// Transform an object — multiply all values by 2
reduce({ a: 1, b: 2, c: 3 }, (acc, val, key) => {
  acc[key] = val * 2;
  return acc;
}, {});
// { a: 2, b: 4, c: 6 }

The third argument is the initial accumulator value — the empty envelope you start with. For sums it’s 0. For object transformations it’s {}. For arrays it’s [].

_.reduceRight does the same thing but right-to-left:

reduceRight([['a', 'b'], ['c', 'd']], (acc, val) => acc.concat(val), []);
// ['c', 'd', 'a', 'b']

Searching

_.find and _.findLast — Find the First (or Last) Match

_.find returns the first element matching your criteria — like scanning a list and stopping as soon as you find what you need.

import find from 'lodash/find';
import findLast from 'lodash/findLast';

const users = [
  { id: 1, name: 'Alice', age: 25 },
  { id: 2, name: 'Bob', age: 30 },
  { id: 3, name: 'Charlie', age: 25 },
];

// Find by matching object properties
find(users, { age: 25 });
// { id: 1, name: 'Alice', age: 25 } — returns FIRST match

// Find with a predicate function
find(users, user => user.name === 'Bob');
// { id: 2, name: 'Bob', age: 30 }

// findLast searches from the END
findLast(users, { age: 25 });
// { id: 3, name: 'Charlie', age: 25 }

Critical distinction: _.find returns a single element (or undefined). _.filter returns an array (possibly empty). Confusing these is one of the most common bugs:

const result = find(users, { age: 25 });
result.name; // Works — 'Alice'

const results = filter(users, { age: 25 });
results.name; // undefined! results is an array, not an object

_.includes — “Is This in There?”

Checks if a value exists in arrays, strings, or objects:

import includes from 'lodash/includes';

includes([1, 2, 3], 2);            // true
includes('hello', 'ell');           // true
includes({ a: 1, b: 2 }, 1);        // true
includes({ a: 1, b: 2 }, 3);        // false

_.some and _.every — “Does Any / Do All Meet the Condition?”

_.some is like asking “is anyone home?” — returns true if at least one passes. _.every is like “did everyone pass the test?” — returns true only if all pass.

import some from 'lodash/some';
import every from 'lodash/every';

some([1, 2, 3], n => n > 2);    // true (3 is > 2)
every([1, 2, 3], n => n > 0);   // true (all are > 0)
every([1, 2, 3], n => n > 2);   // false (1 and 2 are not > 2)

// Works on objects too
every({ a: 'apple', b: 'avocado' }, v => v.startsWith('a')); // true

Grouping & Aggregation

_.groupBy — Sort Items Into Buckets

Think of _.groupBy as sorting laundry. You have a pile of clothes, and you sort them into buckets: whites, darks, delicates. Each bucket gets a label, and all matching items go in.

import groupBy from 'lodash/groupBy';

const words = ['one', 'two', 'three', 'four', 'five'];

// Group by word length
groupBy(words, 'length');
// { '3': ['one', 'two'], '5': ['three'], '4': ['four', 'five'] }

// Group array of objects by a property
const people = [
  { name: 'Alice', role: 'admin' },
  { name: 'Bob', role: 'user' },
  { name: 'Charlie', role: 'admin' },
];

groupBy(people, 'role');
// { admin: [Alice, Charlie], user: [Bob] }

_.keyBy — Create a Lookup Table

Unlike _.groupBy which creates buckets (arrays), _.keyBy creates a dictionary (single object) where each key maps to one item. If two items have the same key, the last one wins.

import keyBy from 'lodash/keyBy';

keyBy(people, 'name');
// {
//   Alice: { name: 'Alice', role: 'admin' },
//   Bob: { name: 'Bob', role: 'user' },
//   Charlie: { name: 'Charlie', role: 'admin' }
// }

// Now you can look up by name instantly:
const lookup = keyBy(people, 'name');
lookup['Alice']; // { name: 'Alice', role: 'admin' }

_.countBy — Tally Occurrences

Like _.groupBy but returns counts instead of arrays:

import countBy from 'lodash/countBy';

countBy(words, 'length');
// { '3': 2, '5': 1, '4': 2 }

countBy([1, 1, 2, 3, 3, 3, 4], n => n);
// { '1': 2, '2': 1, '3': 3, '4': 1 }

Array Operations

_.chunk — Split Into Groups

Splits an array into fixed-size groups — like dividing a deck of cards into piles:

import chunk from 'lodash/chunk';

chunk(['a', 'b', 'c', 'd', 'e'], 2);
// [['a', 'b'], ['c', 'd'], ['e']]

chunk([1, 2, 3, 4], 1);
// [[1], [2], [3], [4]]

_.compact — Remove Falsy Values

Removes all falsy values (false, null, 0, "", undefined, NaN) — like shaking a sieve to let the dust fall through:

import compact from 'lodash/compact';

compact([0, 1, false, 2, '', 3, null, undefined, NaN]);
// [1, 2, 3]

_.uniq — Remove Duplicates

import uniq from 'lodash/uniq';
import sortedUniq from 'lodash/sortedUniq';

uniq([2, 1, 2, 3, 1, 4]);
// [2, 1, 3, 4]

// sortedUniq is faster if your array is already sorted
sortedUniq([1, 1, 2, 3, 3, 4]);
// [1, 2, 3, 4]

// uniqBy for object arrays
uniqBy([{ n: 1 }, { n: 2 }, { n: 1 }], 'n');
// [{ n: 1 }, { n: 2 }]

_.union, _.intersection, _.difference — Set Operations

Like Venn diagram operations for arrays:

import union from 'lodash/union';
import intersection from 'lodash/intersection';
import difference from 'lodash/difference';

const A = [1, 2, 3, 4];
const B = [3, 4, 5, 6];

union(A, B);          // [1, 2, 3, 4, 5, 6] — everything from both
intersection(A, B);   // [3, 4] — present in both
difference(A, B);     // [1, 2] — in A but not B
difference(B, A);     // [5, 6] — in B but not A

_.flatten and _.flattenDeep — Un-nest Arrays

import flatten from 'lodash/flatten';
import flattenDeep from 'lodash/flattenDeep';

flatten([1, [2, [3, [4]], 5]]);
// [1, 2, [3, [4]], 5] — one level only

flattenDeep([1, [2, [3, [4]], 5]]);
// [1, 2, 3, 4, 5] — all the way down

_.sortBy and _.orderBy — Sorting

_.sortBy sorts ascending. _.orderBy lets you specify direction per field:

import sortBy from 'lodash/sortBy';
import orderBy from 'lodash/orderBy';

const users = [
  { name: 'Alice', age: 30 },
  { name: 'Bob', age: 25 },
  { name: 'Charlie', age: 30 },
];

sortBy(users, 'age');
// [Bob(25), Alice(30), Charlie(30)]

orderBy(users, ['age', 'name'], ['asc', 'desc']);
// [Bob(25), Charlie(30), Alice(30)]

Common Mistakes

1. Mutating Source Data in _.reduce

const items = [{ id: 1 }, { id: 2 }];

// ❌ Mutates the original items
const result = reduce(items, (acc, item) => {
  acc[item.id] = item;  // Stores reference to original item
  return acc;
}, {});

// ✅ Creates copies
const result = reduce(items, (acc, item) => ({
  ...acc,
  [item.id]: { ...item }
}), {});

2. Confusing _.find with _.filter

_.find returns one element, _.filter returns an array. Always check which return type your code expects.

3. Relying on _.groupBy Key Order

JavaScript object keys have insertion order for strings, but relying on it is fragile. Use _.orderBy if sort order matters.

4. Using _.chunk with Non-Integer Size

// ❌ chunk('a', 2) would wrap the string
chunk('abc', 2); // ['ab', 'c'] — works on strings too, but may be unexpected

5. Forgetting That _.union Removes Duplicates

_.union returns unique values from all arrays. If you need to preserve duplicates, use _.concat:

// union removes duplicates
union([1, 1, 2], [2, 3]); // [1, 2, 3]

// concat preserves them
[1, 1, 2].concat([2, 3]); // [1, 1, 2, 2, 3]

Practice Questions

1. What does _.compact([0, null, 'hello', undefined, false, 42]) return?

Answer: ['hello', 42]. All falsy values (0, null, undefined, false) are removed.

2. What is the difference between _.find and _.filter?

Answer: _.find returns the first matching element (or undefined). _.filter returns an array of all matching elements (possibly empty).

3. How would you group an array of objects by a property and count occurrences?

Answer: Use _.countBy(collection, 'propertyName'). For example, _.countBy(users, 'role') returns { admin: 2, user: 1 }.

4. Explain _.keyBy vs _.groupBy in one sentence each.

Answer: _.groupBy creates buckets (arrays keyed by category); _.keyBy creates a lookup table (one item per key, last wins).

Challenge

You have an array of file scan results from Durga Antivirus Pro: [{ file: 'a.exe', threat: 'trojan', severity: 8 }, { file: 'b.pdf', threat: null, severity: 0 }, { file: 'c.exe', threat: 'ransomware', severity: 9 }, { file: 'd.doc', threat: null, severity: 0 }]. Use Lodash methods to: filter only files with threats, sort by severity descending, take the top 2 most severe, and extract just their filenames. Expected output: ['c.exe', 'a.exe'].

FAQ

When would I use _.map over native Array.prototype.map?

When you need to iterate over an object’s values, use the property shorthand (_.map(users, 'name')), or need consistency across environments. For simple array mapping in modern browsers, native is preferred.

What is the difference between _.uniq and _.sortedUniq?

_.uniq works on unsorted arrays (O(n) with a Set). _.sortedUniq expects a pre-sorted array and uses a faster algorithm (O(n), single pass).

Does _.flattenDeep handle circular references?

No. _.flattenDeep will throw on circular references. Use a depth-limited approach or a custom function for untrusted data.

How do I get unique objects by a specific property?

Use _.uniqBy(array, 'propertyName'). Example: _.uniqBy([{ id: 1 }, { id: 2 }, { id: 1 }], 'id').

Why does _.reduce need an initial value?

Without an initial value, _.reduce uses the first element as the accumulator — which breaks for empty arrays (throws) and makes the first iteration behave differently. Always pass an initial value for consistency.

Try It Yourself

Build a transformation pipeline with live preview and performance timing:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Lodash Data Transformer</title>
  <script src="https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js"></script>
  <style>
    * { box-sizing: border-box; margin: 0; padding: 0; }
    body { font-family: system-ui, -apple-system, sans-serif; background: #0f172a; color: #e2e8f0; padding: 2rem; }
    .container { max-width: 1200px; margin: 0 auto; }
    h1 { font-size: 1.75rem; margin-bottom: 0.25rem; }
    .subtitle { color: #94a3b8; margin-bottom: 2rem; }
    .layout { display: grid; grid-template-columns: 1fr 1fr; gap: 1.5rem; }
    .card { background: #1e293b; border-radius: 0.75rem; padding: 1.5rem; border: 1px solid #334155; }
    .card h2 { font-size: 1.05rem; margin-bottom: 0.75rem; color: #38bdf8; }
    .card h3 { font-size: 0.9rem; color: #94a3b8; margin-bottom: 0.5rem; text-transform: uppercase; letter-spacing: 0.05em; }
    textarea { width: 100%; background: #0f172a; color: #e2e8f0; border: 1px solid #334155; border-radius: 0.375rem; padding: 0.625rem; font-family: 'JetBrains Mono', monospace; font-size: 0.8125rem; min-height: 150px; resize: vertical; }
    .step { background: #0f172a; border: 1px solid #334155; border-radius: 0.375rem; padding: 0.75rem; margin-bottom: 0.5rem; }
    .step-header { display: flex; gap: 0.5rem; align-items: center; margin-bottom: 0.5rem; flex-wrap: wrap; }
    .step-header select { background: #1e293b; color: #e2e8f0; border: 1px solid #475569; border-radius: 0.25rem; padding: 0.25rem 0.5rem; font-size: 0.8rem; }
    .step-header input { background: #1e293b; color: #e2e8f0; border: 1px solid #475569; border-radius: 0.25rem; padding: 0.25rem 0.5rem; font-size: 0.8rem; flex: 1; min-width: 100px; font-family: monospace; }
    .btn { background: #38bdf8; color: #0f172a; border: none; padding: 0.4rem 1rem; border-radius: 0.375rem; font-weight: 600; cursor: pointer; font-size: 0.85rem; transition: background 0.2s; }
    .btn:hover { background: #7dd3fc; }
    .btn-sm { padding: 0.25rem 0.5rem; font-size: 0.75rem; }
    .btn-outline { background: transparent; color: #38bdf8; border: 1px solid #38bdf8; }
    .btn-outline:hover { background: #38bdf8; color: #0f172a; }
    .btn-danger { background: #ef4444; color: #fff; border: none; padding: 0.4rem 0.75rem; border-radius: 0.375rem; font-weight: 600; cursor: pointer; font-size: 0.75rem; }
    .btn-danger:hover { background: #dc2626; }
    .output-box { background: #0f172a; border: 1px solid #334155; border-radius: 0.375rem; padding: 0.75rem; min-height: 100px; max-height: 300px; overflow: auto; font-family: monospace; font-size: 0.8rem; white-space: pre-wrap; word-break: break-all; margin-top: 0.5rem; }
    .output-box.success { border-color: #22c55e; }
    .output-box.error { border-color: #ef4444; color: #fca5a5; }
    .flex { display: flex; gap: 0.5rem; align-items: center; flex-wrap: wrap; }
    .stat-row { display: flex; gap: 1.5rem; margin-top: 0.75rem; font-size: 0.8rem; }
    .stat-item { color: #94a3b8; }
    .stat-item strong { color: #38bdf8; }
    .pipeline-preview { font-family: monospace; font-size: 0.75rem; color: #94a3b8; margin-top: 0.5rem; padding: 0.5rem; background: #0f172a; border-radius: 0.25rem; border: 1px dashed #334155; }
    .draggable { cursor: grab; }
    .draggable:active { cursor: grabbing; }
    @media (max-width: 768px) { .layout { grid-template-columns: 1fr; } }
  </style>
</head>
<body>
<div class="container">
  <h1>📊 Data Transformer</h1>
  <p class="subtitle">Build a pipeline of Lodash operations and preview results</p>

  <div class="layout">
    <!-- Input -->
    <div class="card">
      <h2>Input Array</h2>
      <textarea id="inputData">[
  { "name": "Alice", "age": 25, "score": 88, "active": true },
  { "name": "Bob", "age": 30, "score": 72, "active": false },
  { "name": "Charlie", "age": 22, "score": 95, "active": true },
  { "name": "Diana", "age": 28, "score": 64, "active": true },
  { "name": "Eve", "age": 35, "score": 91, "active": false },
  { "name": "Frank", "age": 19, "score": 45, "active": true }
]</textarea>
      <div class="flex" style="margin-top:0.5rem;">
        <button class="btn btn-outline btn-sm" onclick="loadSample('users')">Sample: Users</button>
        <button class="btn btn-outline btn-sm" onclick="loadSample('numbers')">Sample: Numbers</button>
        <button class="btn btn-outline btn-sm" onclick="loadSample('words')">Sample: Words</button>
      </div>
    </div>

    <!-- Pipeline -->
    <div class="card">
      <h2>Pipeline</h2>
      <div id="stepsContainer"></div>
      <div class="flex">
        <button class="btn btn-sm" onclick="addStep()">+ Add Step</button>
        <button class="btn btn-outline btn-sm" onclick="resetPipeline()">Reset</button>
      </div>
      <div class="pipeline-preview" id="pipelinePreview">Pipeline: input → (none)</div>
    </div>

    <!-- Output -->
    <div class="card" style="grid-column:1/-1;">
      <div class="flex" style="justify-content:space-between;">
        <h2>Result</h2>
        <button class="btn" onclick="runPipeline()">▶ Run</button>
      </div>
      <div id="outputArea" class="output-box">Click "Run" to execute the pipeline</div>
      <div class="stat-row">
        <span class="stat-item">Items in: <strong id="inCount"></strong></span>
        <span class="stat-item">Items out: <strong id="outCount"></strong></span>
        <span class="stat-item">Time: <strong id="execTime"></strong></span>
        <span class="stat-item">Status: <strong id="execStatus" style="color:#fbbf24;">Ready</strong></span>
      </div>
    </div>
  </div>
</div>

<script>
const INPUT = document.getElementById('inputData');
const STEPS = document.getElementById('stepsContainer');
const PREVIEW = document.getElementById('pipelinePreview');
const OUTPUT = document.getElementById('outputArea');
const IN_CNT = document.getElementById('inCount');
const OUT_CNT = document.getElementById('outCount');
const EX_TIME = document.getElementById('execTime');
const EX_STAT = document.getElementById('execStatus');

const STEP_TYPES = [
  { value: 'filter', label: '_.filter(predicate)', needsArg: true, argPlaceholder: 'x => x.active' },
  { value: 'map', label: '_.map(transform)', needsArg: true, argPlaceholder: 'x => ({...x, bonus: x.score + 5})' },
  { value: 'reject', label: '_.reject(predicate)', needsArg: true, argPlaceholder: 'x => x.age < 21' },
  { value: 'take', label: '_.take(n)', needsArg: true, argPlaceholder: '3' },
  { value: 'sortBy', label: '_.sortBy(key)', needsArg: true, argPlaceholder: "'age'" },
  { value: 'orderBy', label: '_.orderBy(key, dir)', needsArg: true, argPlaceholder: "['age'], ['asc']" },
  { value: 'groupBy', label: '_.groupBy(key)', needsArg: true, argPlaceholder: "'active'" },
  { value: 'uniq', label: '_.uniq', needsArg: false },
  { value: 'uniqBy', label: '_.uniqBy(iteratee)', needsArg: true, argPlaceholder: "'name'" },
  { value: 'compact', label: '_.compact', needsArg: false },
  { value: 'chunk', label: '_.chunk(size)', needsArg: true, argPlaceholder: '2' },
  { value: 'flatten', label: '_.flatten', needsArg: false },
  { value: 'flattenDeep', label: '_.flattenDeep', needsArg: false },
  { value: 'countBy', label: '_.countBy(iteratee)', needsArg: true, argPlaceholder: "'age'" },
  { value: 'keyBy', label: '_.keyBy(key)', needsArg: true, argPlaceholder: "'name'" },
  { value: 'slice', label: '_.slice(start, end)', needsArg: true, argPlaceholder: '0, 3' },
];

function addStep(type, arg) {
  const div = document.createElement('div');
  div.className = 'step';
  div.innerHTML = `
    <div class="step-header">
      <select class="step-type" onchange="onStepChange(this)">
        ${STEP_TYPES.map(t => `<option value="${t.value}" ${t.value === (type || 'filter') ? 'selected' : ''}>${t.label}</option>`).join('')}
      </select>
      <input class="step-arg" placeholder="${STEP_TYPES.find(t => t.value === (type || 'filter'))?.argPlaceholder || ''}" value="${arg || ''}" />
      <button class="btn-danger btn-sm" onclick="this.closest('.step').remove(); updatePreview();">✕</button>
    </div>
  `;
  STEPS.appendChild(div);
  updatePreview();
}

function onStepChange(select) {
  const step = select.closest('.step');
  const input = step.querySelector('.step-arg');
  const type = STEP_TYPES.find(t => t.value === select.value);
  input.placeholder = type?.argPlaceholder || '';
  if (!type?.needsArg) input.style.display = 'none';
  else input.style.display = '';
  updatePreview();
}

function updatePreview() {
  const steps = document.querySelectorAll('.step');
  if (steps.length === 0) { PREVIEW.textContent = 'Pipeline: input → (none)'; return; }
  const parts = ['input'];
  steps.forEach(s => {
    const sel = s.querySelector('.step-type');
    const arg = s.querySelector('.step-arg');
    const label = sel.options[sel.selectedIndex]?.text || sel.value;
    if (arg && arg.value && arg.style.display !== 'none') {
      parts.push(`${sel.value}(${arg.value})`);
    } else {
      parts.push(sel.value);
    }
  });
  PREVIEW.textContent = 'Pipeline: ' + parts.join(' → ');
}

function resetPipeline() {
  STEPS.innerHTML = '';
  addStep('filter', 'x => x.active');
  addStep('map', 'x => ({ name: x.name, score: x.score })');
  addStep('orderBy', "['score'], ['desc']");
  addStep('take', '3');
  updatePreview();
}

function loadSample(type) {
  if (type === 'numbers') {
    INPUT.value = '[' + _.times(20, () => _.random(1, 100)).join(', ') + ']';
  } else if (type === 'words') {
    INPUT.value = JSON.stringify(['apple', 'banana', 'apple', 'cherry', 'banana', 'date', 'elderberry', 'fig', 'grape', 'apple'], null, 2);
  } else {
    INPUT.value = `[
  { "name": "Alice", "age": 25, "score": 88, "active": true },
  { "name": "Bob", "age": 30, "score": 72, "active": false },
  { "name": "Charlie", "age": 22, "score": 95, "active": true },
  { "name": "Diana", "age": 28, "score": 64, "active": true },
  { "name": "Eve", "age": 35, "score": 91, "active": false },
  { "name": "Frank", "age": 19, "score": 45, "active": true }
]`;
  }
}

function runPipeline() {
  let data;
  try { data = JSON.parse(INPUT.value); }
  catch { OUTPUT.textContent = '❌ Invalid JSON in input'; OUTPUT.className = 'output-box error'; return; }

  if (!Array.isArray(data)) { OUTPUT.textContent = '❌ Input must be an array'; OUTPUT.className = 'output-box error'; return; }

  const steps = document.querySelectorAll('.step');
  if (steps.length === 0) { OUTPUT.textContent = JSON.stringify(data, null, 2); OUTPUT.className = 'output-box success'; return; }

  IN_CNT.textContent = data.length;
  EX_STAT.textContent = 'Running...';
  EX_STAT.style.color = '#fbbf24';

  const start = performance.now();
  let result;

  try {
    let chain = _.chain(data);
    steps.forEach(s => {
      const sel = s.querySelector('.step-type');
      const argInput = s.querySelector('.step-arg');
      const op = sel.value;
      const arg = argInput?.value?.trim() || '';
      switch (op) {
        case 'filter': { const fn = arg ? eval(`(${arg})`) : _.identity; chain = chain.filter(fn); break; }
        case 'map': { const fn = arg ? eval(`(${arg})`) : _.identity; chain = chain.map(fn); break; }
        case 'reject': { const fn = arg ? eval(`(${arg})`) : _.identity; chain = chain.reject(fn); break; }
        case 'take': { const n = parseInt(arg) || 1; chain = chain.take(n); break; }
        case 'sortBy': { chain = arg ? chain.sortBy(eval(`(${arg})`)) : chain.sortBy(); break; }
        case 'orderBy': { chain = arg ? chain.orderBy(...eval(`([${arg}])`)) : chain.orderBy(); break; }
        case 'groupBy': { const fn = arg ? eval(`(${arg})`) : _.identity; chain = chain.groupBy(fn); break; }
        case 'uniq': chain = chain.uniq(); break;
        case 'uniqBy': { const fn = arg ? eval(`(${arg})`) : _.identity; chain = chain.uniqBy(fn); break; }
        case 'compact': chain = chain.compact(); break;
        case 'chunk': { const n = parseInt(arg) || 1; chain = chain.chunk(n); break; }
        case 'flatten': chain = chain.flatten(); break;
        case 'flattenDeep': chain = chain.flattenDeep(); break;
        case 'countBy': { const fn = arg ? eval(`(${arg})`) : _.identity; chain = chain.countBy(fn); break; }
        case 'keyBy': { const fn = arg ? eval(`(${arg})`) : _.identity; chain = chain.keyBy(fn); break; }
        case 'slice': { const parts = arg.split(',').map(s => parseInt(s.trim())); chain = chain.slice(parts[0] || 0, parts[1]); break; }
      }
    });
    result = chain.value();
  } catch (e) { OUTPUT.textContent = '❌ Pipeline error: ' + e.message; OUTPUT.className = 'output-box error'; EX_STAT.textContent = 'Error'; EX_STAT.style.color = '#ef4444'; return; }

  const end = performance.now();
  const time = (end - start).toFixed(3);

  OUTPUT.textContent = JSON.stringify(result, null, 2);
  OUTPUT.className = 'output-box success';
  OUT_CNT.textContent = Array.isArray(result) ? result.length : Object.keys(result).length;
  EX_TIME.textContent = time + ' ms';
  EX_STAT.textContent = '✅ Success';
  EX_STAT.style.color = '#22c55e';
}

resetPipeline();
</script>
</body>
</html>

What’s Next

StepTopicWhy
https://tutorials.dodatech.com/frontend/libraries/lodash/lodash-objects-functions/Deep cloning, merging, _.get, _.debounce, _.throttleHandle complex objects and optimize event handling
https://tutorials.dodatech.com/frontend/libraries/lodash/lodash-utilities/String utilities, math, type checking, lazy evaluationComplete your Lodash skillset
https://tutorials.dodatech.com/frontend/libraries/lodash/lodash-getting-started/Installation, imports, chaining fundamentalsReview basics if needed
Node.js Data ProcessingApply Lodash in server-side data pipelinesReal-world backend applications
JSON Data HandlingWorking with JSON data in JavaScriptEssential for API data transformations

What’s Next

Congratulations on completing this Lodash Collections Arrays tutorial! Here’s where to go from here:

  • Practice daily — Consistency is more important than long study sessions
  • Build a project — Apply what you learned by building something real
  • Explore related topics — Check out other tutorials in the same category
  • Join the community — Discuss with other learners and share your progress

Remember: every expert was once a beginner. Keep coding!

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro