Array.prototype.reduce()‎

من موسوعة حسوب

الدالة Array.prototype.reduce()‎ تؤدي إلى اختزال جميع قيم المصفوفة (بدءًا من اليسار إلى اليمين) إلى قيمة واحدة.

const array1 = [1, 2, 3, 4];
const reducer = (accumulator, currentValue) => accumulator + currentValue;

// 1 + 2 + 3 + 4
console.log(array1.reduce(reducer)); // 10

// 5 + 1 + 2 + 3 + 4
console.log(array1.reduce(reducer, 5)); // 15

البنية العامة

arr.reduce(callback[, initialValue])

callback

الدالة التي ستختبر كل عنصر من عناصر المصفوفة، وتقبل أربعة وسائط.

accumulator

القيمة التراكمية، انظر النقاش أدناه للتفاصيل.

currentValue

العنصر الحالي الذي يُعالِج في المصفوفة، وهذه القيمة مطلوبة.

currentIndex

فهرس العنصر الحالي في المصفوفة، وهذه القيمة اختيارية.

array

المصفوفة التي استدعيت الدالة reduce عليها، وهذه القيمة اختيارية.

initialValue

القيمة التي ستُستخدم كوسيط لأوّل استدعاء للدالة callback، وإن لم توفَّر هذه القيمة فسيستخدم أوّل عنصر في المصفوفة.

القيمة المعادة

القيمة الناتجة من عملية الاختزال.

الوصف

الدالة reduce تستدعي الدالة callback مرةً واحدةً لكل عنصر في المصفوفة، باستثناء الفجوات الموجودة في المصفوفة، وهذه الدالة تقبل أربعة وسائط:

  • accumulator
  • currentValue
  • currentIndex
  • array

في أوّل استدعاء للدالة callback، تكون accumulator و currentValue قيمةً من قيمتين. فإذا وفّرنا قيمةً للوسيط initialValue عند استدعاء reduce فستكون accumulator مساويةً إلى initialValue، وستكون currentValue مساويةً إلى أوّل عنصر في المصفوفة؛ أما إذا لم تكن قيمة الوسيط initialValue موجودةً، فستكون accumulator مساويةً لأوّل قيمة في المصفوفة، و currentValue ستساوي القيمة الثانية.

ملاحظة: إذا لم توفَّر قيمة initialValue فستُنفِّذ الدالةُ reduce الدالةَ callback بدءًا من الفهرس 1 وستتجاوز أوّل فهرس، أما إذا كانت القيمة initialValue موجودةً فستبدأ من الفهرس 0.

إذا كانت المصفوفة فارغة ولم توفَّر قيمة initialValue، فسيرمى الاستثناء TypeError، وإذا احتوت المصفوفة على عنصرٍ وحيد (بغض النظر عن موضعه) ولم توفَّر قيمة initialValue أو كانت القيمة initialValue موجودةً لكن المصفوفة فارغة، فستُعاد القيمة الوحيدة دون استدعاء الدالة callback.

من الأفضل توفير قيمة ابتدائية دومًا لأنَّ هنالك ثلاث حالات يمكن أن تحدث إن لم تكن قيمة initialValue مضبوطةً، كما هو موضَّح في المثال الآتي:

var maxCallback = ( acc, cur ) => Math.max( acc.x, cur.x );
var maxCallback2 = ( max, cur ) => Math.max( max, cur );

// دون قيمة ابتدائية
[ { x: 22 }, { x: 42 } ].reduce( maxCallback ); // 42
[ { x: 22 }            ].reduce( maxCallback ); // { x: 22 }
[                      ].reduce( maxCallback ); // TypeError

// مع قيمة ابتدائية
[ { x: 22 }, { x: 42 } ].map( el => el.x )
                        .reduce( maxCallback2, -Infinity );

كيف تعمل الدالة reduce

لنفترض أنَّ لدينا المثال الآتي:

[0, 1, 2, 3, 4].reduce(
  function (
    accumulator,
    currentValue,
    currentIndex,
    array
  ) {
    return accumulator + currentValue;
  }
);

ستُستدعى الدالة callback أربع مرات، وسنستعرض الوسائط الممررة إليها والقيم التي تعيدها في الجدول الآتي:

callback accumulator currentValue currentIndex array القيمة المعادة
أوّل استدعاء 0 1 1 ‎[0, 1, 2, 3, 4]‎‎ 1
ثاني استدعاء 1 2 2 ‎[0, 1, 2, 3, 4]‎‎ 3
ثالث استدعاء 3 3 3 ‎[0, 1, 2, 3, 4]‎‎ 6
رابع استدعاء 6 4 4 ‎[0, 1, 2, 3, 4]‎‎ 10

القيمة المعادة من الدالة reduce هي آخر قيمة مُعادة من الدالة callback (وهي 10).

يمكنك أيضًا استخدام الدوال السهمية بدلًا من استخدام دالة كاملة، فالمثال السابق يُنتِج نفس النتيجة السابقة:

[0, 1, 2, 3, 4].reduce( (prev, curr) => prev + curr );

أما إذا وفّرتَ قيمةً ابتدائيةً كوسيطٍ ثانٍ للدالة reduce، أي أنَّ الشيفرة كالآتية:

[0, 1, 2, 3, 4].reduce(
  (accumulator, currentValue, currentIndex, array) => {
    return accumulator + currentValue;
  },
  10
);

والجدول:

callback accumulator currentValue currentIndex array القيمة المعادة
أوّل استدعاء 10 0 0 ‎[0, 1, 2, 3, 4]‎‎ 10
ثاني استدعاء 10 1 1 ‎[0, 1, 2, 3, 4]‎‎ 11
ثالث استدعاء 11 2 2 ‎[0, 1, 2, 3, 4]‎‎ 13
رابع استدعاء 16 4 4 ‎[0, 1, 2, 3, 4]‎‎ 20

القيمة المعادة من الدالة reduce في هذه الحالة هي 20.

أمثلة

جمع كل القيم في المصفوفة

var sum = [0, 1, 2, 3].reduce(function (a, b) {
  return a + b;
}, 0);
// 6

يمكن كتابة الشيفرة السابقة باستخدام دالة سهمية:

var total = [ 0, 1, 2, 3 ].reduce(
  ( acc, cur ) => acc + cur,
  0
);

تسطيح مصفوفة من المصفوفات

var flattened = [[0, 1], [2, 3], [4, 5]].reduce(
  function(a, b) {
    return a.concat(b);
  },
  []
);
// [0, 1, 2, 3, 4, 5]

يمكن كتابة الشيفرة السابقة باستخدام دالة سهمية:

var flattened = [[0, 1], [2, 3], [4, 5]].reduce(
  ( acc, cur ) => acc.concat(cur),
  []
);

إحصاء عدد تكرار عناصر المصفوفة

var names = ['Alice', 'Bob', 'Tiff', 'Bruce', 'Alice'];

var countedNames = names.reduce(function (allNames, name) { 
  if (name in allNames) {
    allNames[name]++;
  }
  else {
    allNames[name] = 1;
  }
  return allNames;
}, {});
// { 'Alice': 2, 'Bob': 1, 'Tiff': 1, 'Bruce': 1 }

تعويض نقص دعم المتصفحات

أضيفت هذه الدالة في الإصدار الخامس من معيار ECMAScript، لذا يمكنك استخدام الشيفرة الآتية لإضافتها للمتصفحات التي لا تدعمها:

// Production steps of ECMA-262, Edition 5, 15.4.4.21
// Reference: http://es5.github.io/#x15.4.4.21
// https://tc39.github.io/ecma262/#sec-array.prototype.reduce
if (!Array.prototype.reduce) {
  Object.defineProperty(Array.prototype, 'reduce', {
    value: function(callback /*, initialValue*/) {
      if (this === null) {
        throw new TypeError( 'Array.prototype.reduce ' + 
          'called on null or undefined' );
      }
      if (typeof callback !== 'function') {
        throw new TypeError( callback +
          ' is not a function');
      }

      // 1. Let O be ? ToObject(this value).
      var o = Object(this);

      // 2. Let len be ? ToLength(? Get(O, "length")).
      var len = o.length >>> 0; 

      // Steps 3, 4, 5, 6, 7      
      var k = 0; 
      var value;

      if (arguments.length >= 2) {
        value = arguments[1];
      } else {
        while (k < len && !(k in o)) {
          k++; 
        }

        // 3. If len is 0 and initialValue is not present,
        //    throw a TypeError exception.
        if (k >= len) {
          throw new TypeError( 'Reduce of empty array ' +
            'with no initial value' );
        }
        value = o[k++];
      }

      // 8. Repeat, while k < len
      while (k < len) {
        // a. Let Pk be ! ToString(k).
        // b. Let kPresent be ? HasProperty(O, Pk).
        // c. If kPresent is true, then
        //    i.  Let kValue be ? Get(O, Pk).
        //    ii. Let accumulator be ? Call(
        //          callbackfn, undefined,
        //          « accumulator, kValue, k, O »).
        if (k in o) {
          value = callback(value, o[k], k, o);
        }

        // d. Increase k by 1.      
        k++;
      }

      // 9. Return accumulator.
      return value;
    }
  });
}

دعم المتصفحات

الميزة Chrome Firefox Internet Explorer Opera Safari
الدعم الأساسي نعم نعم 9 نعم 4

مصادر ومواصفات