سؤال

أرغب في تصفية مجموعة من العناصر باستخدام map() وظيفة.فيما يلي مقتطف التعليمات البرمجية:

var filteredItems = items.map(function(item)
{
    if( ...some condition... )
    {
        return item;
    }
});

تكمن المشكلة في أن العناصر التي تمت تصفيتها لا تزال تستهلك مساحة في المصفوفة وأرغب في مسحها بالكامل.

اي فكرة؟

يحرر:شكرا، لقد نسيت filter(), ، ما أردته هو في الواقع أ filter() ثم map().

تحرير 2:شكرا للإشارة إلى ذلك map() و filter() لا يتم تنفيذها في جميع المتصفحات، على الرغم من أن الكود المحدد الخاص بي لم يكن مخصصًا للتشغيل في المتصفح.

هل كانت مفيدة؟

المحلول

يجب عليك استخدام filter الطريقة بدلاً من الخريطة إلا إذا كنت تريد تغيير العناصر الموجودة في المصفوفة، بالإضافة إلى التصفية.

على سبيل المثال.

var filteredItems = items.filter(function(item)
{
    return ...some condition...;
});

[يحرر:بالطبع يمكنك أن تفعل دائما sourceArray.filter(...).map(...) لكل من التصفية والتحور]

نصائح أخرى

لقد كتبت إجابة منذ فترة، وتغيرت آرائي.أوصي بمراجعة حسابي مشاركة مدونة مما يوسع هذا الموضوع ويشرحه بشكل أفضل.كما أنه يعطي مقارنة JSperf في نهاية البدائل.

tl;dr هو هذا:لإنجاز ما تطلبه (التصفية ورسم الخرائط ضمن استدعاء دالة واحد)، يجب عليك استخدام Array.reduce().ومع ذلك، أكثر قابلية للقراءة و عادة أسرع2 النهج هو مجرد استخدام الفلتر والخريطة بالسلاسل معًا:

[1,2,3].filter(num => num > 2).map(num => num * 2)

وفيما يلي وصف لكيفية ذلك Array.reduce() يعمل، وكيف يمكن استخدامه لإنجاز التصفية والخريطة في تكرار واحد.إذا كان هذا مكثفًا للغاية، فإنني أوصي بشدة بمشاهدة مشاركة المدونة المرتبطة أعلاه، والتي تعد مقدمة أكثر ودية مع أمثلة واضحة وتقدم.

يمكنك تقليل الوسيطة التي تكون وظيفة (مجهولة عادةً).

تلك الوظيفة المجهولة يأخذ معلمتين - أحدهما (مثل الوظائف المجهولة التي تم تمريرها إلى Map/filter/forEach) هو التكرار الذي سيتم التشغيل عليه.هناك حجة أخرى للدالة المجهولة التي تم تمريرها للتقليل من أن تلك الوظائف لا تقبلها، وهي القيمة التي سيتم تمريرها بين استدعاءات الوظائف، والتي يشار إليها غالبًا باسم مذكرة.

لاحظ أنه في حين أن Array.filter() يأخذ وسيطة واحدة فقط (دالة)، فإن Array.reduce() يأخذ أيضًا وسيطة ثانية مهمة (رغم أنها اختيارية):قيمة أولية لـ "memo" سيتم تمريرها إلى تلك الوظيفة المجهولة كوسيطة أولى لها، ويمكن بعد ذلك تعديلها وتمريرها بين استدعاءات الوظائف.(إذا لم يتم توفيرها، فستكون "المذكرة" في أول استدعاء دالة مجهولة هي التكرار الأول افتراضيًا، وستكون الوسيطة "التكرار" هي القيمة الثانية في الصفيف)

في حالتنا، سنمرر مصفوفة فارغة للبدء، ثم نختار ما إذا كنا سنحقن تكرارنا في مصفوفتنا أم لا بناءً على وظيفتنا - هذه هي عملية التصفية.

أخيرًا، سنعيد "المصفوفة قيد التقدم" الخاصة بنا في كل استدعاء دالة مجهولة، وسيأخذ التخفيض قيمة الإرجاع هذه ويمررها كوسيطة (تسمى مذكرة) إلى استدعاء الدالة التالي.

يسمح هذا بإجراء التصفية والخريطة في تكرار واحد، مما يقلل عدد التكرارات المطلوبة إلى النصف.:)

للحصول على شرح أكثر اكتمالا، راجع MDN أو الرابط أعلاه.:)

مثال أساسي على تقليل المكالمة:

let array = [1,2,3];
const initialMemo = [];

array = array.reduce((memo, iteratee) => {
    // if condition is our filter
    if (iteratee > 1) {
        // what happens inside the filter is the map
        memo.push(iteratee * 2); 
    }

    // this return value will be passed in as the 'memo' argument
    // to the next call of this function, and this function will have
    // every element passed into it at some point.
    return memo; 
}, initialMemo)

console.log(array) // [4,6], equivalent to [(2 * 2), (3 * 2)]

نسخة أكثر إيجازا:

[1,2,3].reduce((memo, value) => value > 1 ? memo.concat(value * 2) : memo, [])

لاحظ أن التكرار الأول لم يكن أكبر من واحد، وبالتالي تمت تصفيته.لاحظ أيضًا المذكرة الأولية، والتي تم تسميتها فقط لتوضيح وجودها ولفت الانتباه إليها.مرة أخرى، يتم تمريرها كـ "مذكرة" إلى أول استدعاء دالة مجهولة، ثم يتم تمرير القيمة التي تم إرجاعها للوظيفة المجهولة كوسيطة "مذكرة" إلى الوظيفة التالية.

مثال آخر لحالة الاستخدام الكلاسيكي للمذكرة هو إرجاع الرقم الأصغر أو الأكبر في المصفوفة.مثال:

[7,4,1,99,57,2,1,100].reduce((memo, val) => memo > val ? memo : val)
// ^this would return the largest number in the list.

مثال على كيفية كتابة دالة التخفيض الخاصة بك (وهذا غالبًا ما يساعد في فهم وظائف مثل هذه، كما أجد):

test_arr = [];

// we accept an anonymous function, and an optional 'initial memo' value.
test_arr.my_reducer = function(reduceFunc, initialMemo) {
    // if we did not pass in a second argument, then our first memo value 
    // will be whatever is in index zero. (Otherwise, it will 
    // be that second argument.)
    const initialMemoIsIndexZero = arguments.length < 2;

    // here we use that logic to set the memo value accordingly.
    let memo = initialMemoIsIndexZero ? this[0] : initialMemo;

    // here we use that same boolean to decide whether the first
    // value we pass in as iteratee is either the first or second
    // element
    const initialIteratee = initialMemoIsIndexZero ? 1 : 0;

    for (var i = initialIteratee; i < this.length; i++) {
        // memo is either the argument passed in above, or the 
        // first item in the list. initialIteratee is either the
        // first item in the list, or the second item in the list.
        memo = reduceFunc(memo, this[i]);
    }

    // after we've compressed the array into a single value,
    // we return it.
    return memo;
}

يسمح التنفيذ الحقيقي بالوصول إلى أشياء مثل الفهرس، على سبيل المثال، ولكنني آمل أن يساعدك هذا في الحصول على فكرة غير معقدة عن جوهر الأمر.

هذا ليس ما تفعله الخريطة.هل تريد حقا Array.filter.أو إذا كنت تريد حقًا إزالة العناصر من القائمة الأصلية، فسوف تحتاج إلى القيام بذلك بشكل حتمي باستخدام حلقة for.

ومع ذلك، يجب أن تلاحظ أن Array.filter غير مدعوم في جميع المتصفحات، لذا يجب عليك إنشاء نموذج أولي:

//This prototype is provided by the Mozilla foundation and
//is distributed under the MIT license.
//http://www.ibiblio.org/pub/Linux/LICENSES/mit.license

if (!Array.prototype.filter)
{
    Array.prototype.filter = function(fun /*, thisp*/)
    {
        var len = this.length;

        if (typeof fun != "function")
            throw new TypeError();

        var res = new Array();
        var thisp = arguments[1];

        for (var i = 0; i < len; i++)
        {
            if (i in this)
            {
                var val = this[i]; // in case fun mutates this

                if (fun.call(thisp, val, i, this))
                   res.push(val);
            }
        }

        return res;
    };
}

ومن خلال القيام بذلك، يمكنك إنشاء نموذج أولي لأي طريقة قد تحتاجها.

أضع هذه الإجابة هنا لأن polyfills المشتركة في هذه الصفحة سيئة للغاية

function reduce(f, y, xs, context) {
  var acc = y;
  for (var i = 0, len = xs.length; i < len; i++)
    acc = f.call(context, acc, xs[i], i, xs);
  return acc;
}

function reduce1(f, xs, context) {
  if (xs.length === 0)
    throw Error('cannot reduce empty array without initial value');
  else
    return reduce(f, xs[0], xs.slice(1), context);
}

function map(f, xs, context) {
  return reduce(function(acc, x, i) {
    return acc.concat([
      f.call(context, x, i, xs)
    ]);
  }, [], xs);
}

function filter(f, xs, context) {
  return reduce(function(acc, x, i) {
    if (f.call(context, x, i, xs))
      return acc.concat([x]);
    else
      return acc;
  }, [], xs);
}

تمديد النماذج الأولية

if (Array.prototype.reduce === undefined) {
  Array.prototype.reduce = function(f, initialValue, context) {
    if (initialValue === undefined)
      return reduce1(f, this, context);
    else
      return reduce(f, initialValue, this, context);
  };
}

if (Array.prototype.map === undefined) {
  Array.prototype.map = function(f, context) {
    return map(f, this, context);
  };
}

if (Array.prototype.filter === undefined) {
  Array.prototype.filter = function(f, context) {
    return filter(f, this, context);
  };
}
var arr = [1,2,'xxx','yyy']

arr = arr.filter(function(e){ return e != 'xxx' });

arr  // [1, 2, "yyy"]
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top