📚معرفی و نصب

Lodash یک کتابخانه Utility برای جاوااسکریپت است که بیش از ۳۰۰ تابع آماده برای کار با آرایه، شیء، رشته و تابع فراهم می‌کند. عملیاتی که با JS خالص ۱۰ خط نیاز دارد، با Lodash ۱ خط است.

🔒
ایمنی null بدون خطای undefined
عملکرد بالا lazy evaluation
📋
Immutable داده اصلی دست نخورد
🔌
+300 تابع برای هر نیازی

نصب

<!-- فایل لوکال -->
<script src="lodash.min.js"></script>

// استفاده با _ (underscore)
_.chunk([1,2,3,4], 2);  // → [[1,2],[3,4]]
_.sum([1,2,3]);          // → 6

// NPM / ES Module
import _ from 'lodash'
// یا فقط تابع موردنیاز (tree-shaking)
import chunk from 'lodash/chunk'
ⓘ نکته: Lodash از طریق متغیر سراسری _ در دسترس است. در پروژه‌های بزرگ برای کاهش bundle size، فقط توابع موردنیاز را import کنید.

📈توابع آرایه (Array)

تابعتوضیحمثال
_.chunk(arr, n)تقسیم به قطعات n‌تایی[1,2,3,4] → [[1,2],[3,4]]
_.compact(arr)حذف مقادیر falsy[0,'',false,1] → [1]
_.uniq(arr)حذف تکراری‌ها[1,2,1,3] → [1,2,3]
_.uniqBy(arr, key)حذف تکراری بر اساس کلیدuniqBy(users,'id')
_.flatten(arr)flat یک سطح[[1,2],[3]] → [1,2,3]
_.flattenDeep(arr)flat کامل عمیق[1,[2,[3]]] → [1,2,3]
_.difference(a, b)اختلاف دو آرایه[1,2,3],[2] → [1,3]
_.intersection(a, b)اشتراک دو آرایه[1,2],[2,3] → [2]
_.union(a, b)اجتماع بدون تکرار[1,2],[2,3] → [1,2,3]
_.shuffle(arr)درهم‌ریختن تصادفی_.shuffle([1,2,3,4,5])
_.sample(arr)یک عنصر تصادفی_.sample([1,2,3]) → 2
_.sampleSize(arr, n)n عنصر تصادفی_.sampleSize([1..5], 3)
_.take(arr, n)n عنصر اول_.take([1,2,3,4], 2) → [1,2]
_.drop(arr, n)حذف n عنصر اول_.drop([1,2,3,4], 2) → [3,4]
_.zip / _.unzipترکیب/جدا کردن آرایه‌ها_.zip([1,2],['a','b']) → [[1,'a'],[2,'b']]
▶ آزمایشگاه: توابع آرایه
نتیجه اینجا نشان داده می‌شود...

📋توابع Collection

این توابع روی هر دو آرایه و شیء کار می‌کنند — ایده‌آل برای پردازش داده.

تابعتوضیح
_.groupBy(col, key)گروه‌بندی بر اساس کلید یا تابع
_.sortBy(col, key)مرتب‌سازی صعودی
_.orderBy(col, keys, dirs)مرتب‌سازی با جهت asc/desc
_.countBy(col, fn)شمارش بر اساس معیار
_.filter(col, predicate)فیلتر با شرط / شیء
_.find(col, predicate)اولین مورد منطبق
_.map(col, fn)تبدیل هر آیتم
_.reduce(col, fn, init)تجمیع
_.every / _.someهمه / حداقل یکی شرط را دارند
_.keyBy(col, key)تبدیل آرایه به شیء با کلید
_.flatMap(col, fn)map + flatten
const students = [
  { name: 'علی',    grade: 'A', score: 95, city: 'تهران' },
  { name: 'سارا',   grade: 'B', score: 82, city: 'اصفهان' },
  { name: 'رضا',    grade: 'A', score: 88, city: 'تهران' },
  { name: 'مینا',   grade: 'B', score: 75, city: 'مشهد' },
  { name: 'حسین',  grade: 'A', score: 72, city: 'اصفهان' },
  { name: 'فاطمه', grade: 'B', score: 91, city: 'تهران' },
];

_.groupBy(students, 'grade');
// → { A: [{علی},{رضا},{حسین}], B: [{سارا},{مینا},{فاطمه}] }

_.orderBy(students, ['score'], ['desc']);
// → [{علی,95},{فاطمه,91},{رضا,88},...]

_.keyBy(students, 'name');
// → { 'علی': {name:'علی',...}, 'سارا': {...} }
▶ آزمایشگاه: Collection با داده دانشجویان
نتیجه اینجا نشان داده می‌شود...

💾توابع Object (شیء)

const obj = { a: 1, b: { c: 2, d: 3 }, e: [4,5] };

// کپی عمیق — بدون اشاره به اصل
const copy = _.cloneDeep(obj);

// دسترسی ایمن به nested — بدون خطای TypeError
_.get(obj, 'b.c');              // → 2
_.get(obj, 'x.y.z', 'default'); // → 'default' (نه خطا!)

// تنظیم مقدار nested
_.set(obj, 'b.e', 99);

// ادغام عمیق
_.merge({ a:1, b:{ x:1 } }, { b:{ y:2 }, c:3 });
// → { a:1, b:{x:1,y:2}, c:3 }

// انتخاب / حذف کلیدها
_.pick(obj, ['a', 'e']);  // → { a:1, e:[4,5] }
_.omit(obj, ['a']);      // → { b:{c:2,d:3}, e:[4,5] }

// مقایسه عمیق
_.isEqual({ a:1 }, { a:1 }); // → true (برخلاف ===)

// کلیدها / مقادیر / جفت‌ها
_.keys(obj);    // → ['a','b','e']
_.values(obj);  // → [1,{c:2,d:3},[4,5]]
_.toPairs(obj); // → [['a',1],['b',{...}],...]

// مقدار پیش‌فرض — فقط اگر undefined باشد
_.defaults({ a:1 }, { a:99, b:2 }); // → { a:1, b:2 }
▶ آزمایشگاه: توابع Object
نتیجه اینجا نشان داده می‌شود...

🔡توابع String (رشته)

تابعتوضیحخروجی نمونه
_.camelCase(str)به camelCasefooBarBaz
_.kebabCase(str)به kebab-casefoo-bar-baz
_.snakeCase(str)به snake_casefoo_bar_baz
_.startCase(str)هر کلمه بزرگFoo Bar Baz
_.capitalize(str)اول بزرگ، بقیه کوچکHello world
_.truncate(str,opts)برش با ...Hello Wor...
_.pad(str,len,char)پد از دو طرف--hi--
_.padStart/Endپد از ابتدا/انتها00042
_.repeat(str,n)تکرار رشتهha ha ha
_.escape(str)escape HTML&lt;script&gt;
_.words(str)جدا کردن کلمات['foo','bar']
▶ آزمایشگاه: توابع String
نتیجه اینجا نشان داده می‌شود...

debounce و throttle

ⓘ تفاوت:  debounce — تابع را فقط پس از اینکه برای مدت X دیگر فراخوانی نشود اجرا می‌کند (مثل جستجو: بعد از اتمام تایپ).  throttle — تابع را حداکثر هر X میلی‌ثانیه یک‌بار اجرا می‌کند (مثل scroll: حداکثر ۵ بار در ثانیه).
// debounce — جستجو: فقط بعد از 500ms سکوت
const search = _.debounce((query) => {
  fetchAPI(query);   // درخواست به سرور
}, 500);
input.addEventListener('input', e => search(e.target.value));

// throttle — scroll: حداکثر هر 200ms
const onScroll = _.throttle(() => {
  console.log('scroll:', window.scrollY);
}, 200);
window.addEventListener('scroll', onScroll);

// لغو
search.cancel();   // لغو debounce در حال انتظار
search.flush();    // اجرای فوری بدون انتظار

// once — فقط یک بار اجرا
const init = _.once(() => {
  console.log('فقط یک بار!');
});
▶ آزمایشگاه زنده: در کادر تایپ کنید
بدون کنترل (هر keystroke)
0
بار فراخوانی شد
debounce (600ms بعد از توقف)
0
بار اجرا شد
throttle (حداکثر هر 500ms)
0
بار اجرا شد

🔢Math و Utility

// ─── Math ───
_.random(1, 100);            // عدد تصادفی صحیح بین ۱-۱۰۰
_.random(1.0, 5.0);         // عدد تصادفی اعشاری
_.clamp(120, 0, 100);        // → 100  (محدود بین min/max)
_.clamp(-5,  0, 100);        // → 0
_.inRange(5, 1, 10);         // → true
_.sum([1,2,3]);               // → 6
_.mean([1,2,3]);              // → 2

// ─── Type Checking ───
_.isArray([1,2]);             // → true
_.isObject({});               // → true
_.isString('hi');            // → true
_.isNumber(42);              // → true
_.isFunction(() => {});       // → true
_.isNil(null);               // → true (null یا undefined)
_.isEmpty([]);                // → true
_.isEmpty({});                // → true
_.isEmpty('');               // → true

// ─── Utility ───
_.times(5, i => i * 2);      // → [0,2,4,6,8]
_.range(1, 6);               // → [1,2,3,4,5]
_.range(0, 10, 2);            // → [0,2,4,6,8] (با گام ۲)
_.uniqueId('item_');         // → 'item_1', 'item_2', ...
_.noop();                     // تابع خالی
_.identity(42);              // → 42 (همان ورودی)
_.memoize(expensiveFn);       // کش کردن نتیجه تابع
▶ آزمایشگاه: Math و Utility
نتیجه اینجا نشان داده می‌شود...

🏆پروژه کامل — تحلیل داده

ترکیب چندین تابع Lodash برای تحلیل یک لیست سفارشات:

const orders = [
  { id:1, user:'علی',    city:'تهران',   product:'لپ‌تاپ',  price:35000000, qty:1 },
  { id:2, user:'سارا',   city:'اصفهان',  product:'موبایل',  price:18000000, qty:2 },
  { id:3, user:'رضا',    city:'تهران',   product:'تبلت',   price:12000000, qty:3 },
  { id:4, user:'مینا',   city:'مشهد',    product:'لپ‌تاپ',  price:42000000, qty:1 },
  { id:5, user:'حسین',  city:'اصفهان',  product:'موبایل',  price:22000000, qty:1 },
  { id:6, user:'فاطمه', city:'تهران',   product:'هدفون',   price:4500000,  qty:4 },
];

// ۱. مجموع فروش هر شهر
const byCity = _.groupBy(orders, 'city');
const cityRevenue = _.mapValues(byCity, items =>
  _.sumBy(items, o => o.price * o.qty)
);

// ۲. پرفروش‌ترین محصول
const topProduct = _.maxBy(
  _.map(_.groupBy(orders, 'product'), (items, name) => ({
    name, total: _.sumBy(items, o => o.price * o.qty)
  })),
  'total'
);

// ۳. مرتب‌سازی سفارشات بر اساس ارزش کل (نزولی)
const sorted = _.orderBy(orders,
  [o => o.price * o.qty], ['desc']
);
▶ دمو: تحلیل سفارشات زنده
یک دکمه را کلیک کنید...
💡 خلاصه Lodash در یک نگاه:
  • _.groupBy / _.orderBy / _.countBy — تجزیه و تحلیل داده
  • _.get / _.set / _.cloneDeep — کار ایمن با object
  • _.debounce / _.throttle — کنترل عملکرد
  • _.chunk / _.uniq / _.shuffle — عملیات آرایه
  • _.camelCase / _.truncate — پردازش رشته
  • _.isNil / _.isEmpty / _.isEqual — بررسی نوع و مقدار

🚀زمین بازی کد (Playground)

کد Lodash را اجرا کن و خروجی را در پنل کنسول ببین. توابع آرایه، collection و object را امتحان کن. نسخهٔ ۴.۱۷.۲۱ آفلاین بارگذاری می‌شود.