.Map () में एक तत्व पर कैसे छोड़ें?


417

मैं किसी ऐरे तत्व को कैसे छोड़ सकता हूँ .map?

मेरा कोड:

var sources = images.map(function (img) {
    if(img.src.split('.').pop() === "json"){ // if extension is .json
        return null; // skip
    }
    else{
        return img.src;
    }
});

यह लौटेगा:

["img.png", null, "img.png"]

18
आप नहीं कर सकते, लेकिन आप बाद में सभी अशक्त मूल्यों को फ़िल्टर कर सकते हैं।
फेलिक्स क्लिंग

1
क्यों नहीं? मुझे पता है कि जारी रखने से काम नहीं चलता है, लेकिन यह जानना अच्छा होगा कि क्यों (डबल लूपिंग से भी बचना होगा) - संपादित करें - अपने मामले के लिए आप सिर्फ अगर स्थिति को उल्टा नहीं कर सकते हैं और केवल तभी img.srcविभाजित कर सकते हैं जब विभाजन पॉप का परिणाम! = = जसन?
ग्रेवडॉक्स

@GrayedFox तब निहितार्थ के undefinedबजाय सरणी में डाल दिया जाएगा null। बेहतर नहीं है ...
FZs

जवाबों:


637

बस .filter()यह पहले:

var sources = images.filter(function(img) {
  if (img.src.split('.').pop() === "json") {
    return false; // skip
  }
  return true;
}).map(function(img) { return img.src; });

यदि आप ऐसा नहीं करना चाहते हैं, जो अनुचित नहीं है क्योंकि इसकी कुछ लागत है, तो आप अधिक सामान्य उपयोग कर सकते हैं .reduce()। आप आम तौर पर के .map()संदर्भ में व्यक्त कर सकते हैं .reduce:

someArray.map(function(element) {
  return transform(element);
});

के रूप में लिखा जा सकता है

someArray.reduce(function(result, element) {
  result.push(transform(element));
  return result;
}, []);

इसलिए यदि आपको तत्वों को छोड़ने की आवश्यकता है, तो आप ऐसा आसानी से कर सकते हैं .reduce():

var sources = images.reduce(function(result, img) {
  if (img.src.split('.').pop() !== "json") {
    result.push(img.src);
  }
  return result;
}, []);

उस संस्करण में, .filter()पहले नमूने से कोड .reduce()कॉलबैक का हिस्सा है । छवि स्रोत केवल मामले में परिणाम सरणी पर धकेल दिया जाता है जहां फ़िल्टर ऑपरेशन ने इसे रखा होगा।


21
क्या आपको पूरे सरणी पर दो बार लूप की आवश्यकता नहीं है? क्या इससे बचने का कोई उपाय है?
एलेक्स मैकमिलन

7
@AlexMcMillan आप उपयोग कर सकते हैं .reduce()और यह सब एक पास में कर सकते हैं , हालांकि प्रदर्शन-वार मुझे संदेह है कि यह एक महत्वपूर्ण अंतर होगा।
नुकीले

9
इन सभी नकारात्मक के साथ, "खाली" शैली मूल्यों ( null, undefined, NaNअगर हम एक के अंदर एक प्रयोग कर सकते हैं आदि) यह अच्छा होगा map()एक संकेत है कि इस वस्तु कुछ भी नहीं करने के लिए नक्शे और छोड़े गए किया जाना चाहिए के रूप में। मैं अक्सर उन सरणियों में आता हूं जिन्हें मैं 98% मैप करना चाहता हूं (जैसे: String.split()अंत में एक एकल, खाली स्ट्रिंग छोड़ता हूं, जिसकी मुझे परवाह नहीं है)। आपके उत्तर के लिए धन्यवाद :)
एलेक्स मैकमिलन

6
@AlexMcMillan अच्छी .reduce()तरह से आधारभूत है "जो भी आप चाहते हैं" फ़ंक्शन करें, क्योंकि आपके पास वापसी मूल्य पर पूरा नियंत्रण है। ट्रांसड्यूसर्स की अवधारणा के बारे में क्लोजर में रिच हिकी द्वारा उत्कृष्ट कार्य में आपकी रुचि हो सकती है ।
पॉंच

3
@vsync आप एक तत्व को छोड़ नहीं सकते .map()। आप .reduce()इसके बजाय उपयोग कर सकते हैं , इसलिए मैं इसे जोड़ूंगा।
इंगित

25

मुझे लगता है कि फिल्टर () विधि का उपयोग करके किसी सरणी से कुछ तत्वों को छोड़ने का सबसे सरल तरीका है ।

इस विधि ( ES5 ) और ES6 सिंटैक्स का उपयोग करके आप एक पंक्ति में अपना कोड लिख सकते हैं , और यह वही होगा जो आप चाहते हैं :

let images = [{src: 'img.png'}, {src: 'j1.json'}, {src: 'img.png'}, {src: 'j2.json'}];

let sources = images.filter(img => img.src.slice(-4) != 'json').map(img => img.src);

console.log(sources);


1
ठीक यही .filter()बात है
हिमस्खलन 1

2
क्या यह forEachदो के बजाय एक पास से बेहतर और पूरा हो गया है?
वुलिवोंग

1
जैसी आपकी इच्छा हो @wuliwong। लेकिन कृपया ध्यान रखें कि यह अभी भी O(n)जटिलता के खानों में होगा और कृपया कम से कम इन दो लेखों को भी देखें: frontendcollisionblog.com/javascript/2015/08/15/… और coderwall.com/p/kvzbpa/don-t- उपयोग-एरे-फॉरच-यूज-फॉर- ऑल - बेस्ट!
simhumileco

1
शुक्रिया @simhumileco! उसके कारण, मैं यहाँ हूँ (और शायद कई अन्य लोगों के रूप में भी)। सवाल यह है कि केवल एक बार पुनरावृत्ति करके .filter और .map को कैसे संयोजित किया जाए।
जैक ब्लैक

21

2019 के बाद से, Array.prototype.flatMap एक अच्छा विकल्प है।

images.flatMap(({src}) => src.endsWith('.json') ? [] : src);

MDN से :

flatMapमानचित्र के दौरान वस्तुओं को जोड़ने और हटाने के तरीके के रूप में उपयोग किया जा सकता है। दूसरे शब्दों में, यह आपको हमेशा एक-से-एक के बजाय कई वस्तुओं को कई मदों (प्रत्येक इनपुट आइटम को अलग से संभालकर) को मैप करने की अनुमति देता है। इस अर्थ में, यह फिल्टर के विपरीत की तरह काम करता है। केवल आइटम रखने के लिए 1-तत्व सरणी लौटें, आइटम जोड़ने के लिए एक बहु-तत्व सरणी या आइटम को निकालने के लिए 0-तत्व सरणी।


1
सबसे अच्छा जवाब हाथ नीचे! यहाँ अधिक जानकारी: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
डोमिनिक परेटटी

1
यह वास्तव में सरल और मजबूत जवाब है। हम सीखते हैं कि यह फिल्टर और कम करने से बेहतर है।
ओर्का

19

TLDR: आप पहले अपने सरणी को फ़िल्टर कर सकते हैं और फिर अपना मानचित्र प्रदर्शन कर सकते हैं लेकिन इसके लिए सरणी पर दो पास की आवश्यकता होगी (फ़िल्टर मैप करने के लिए एक सरणी देता है)। चूंकि यह सरणी छोटा है, इसलिए यह बहुत छोटी प्रदर्शन लागत है। आप एक सरल कम भी कर सकते हैं। हालाँकि यदि आप फिर से कल्पना करना चाहते हैं कि यह सरणी (या किसी भी डेटाटाइप) पर एक पास के साथ कैसे किया जा सकता है, तो आप रिच हिकी द्वारा लोकप्रिय "ट्रांसड्यूसर" नामक एक विचार का उपयोग कर सकते हैं।

उत्तर:

हमें सरणी पर बढ़ते हुए डॉट चाइनिंग और संचालन की आवश्यकता नहीं होनी चाहिए [].map(fn1).filter(f2)...क्योंकि यह दृष्टिकोण हर reducingफ़ंक्शन पर मेमोरी में मध्यवर्ती सरणियों का निर्माण करता है।

सबसे अच्छा दृष्टिकोण वास्तविक कम करने वाले फ़ंक्शन पर काम करता है, इसलिए डेटा का केवल एक पास है और कोई अतिरिक्त सरणियां नहीं हैं।

रिड्यूसिंग फंक्शन वह फंक्शन है जिसे पास किया reduceजाता है और सोर्स से एक संचायक और इनपुट लेता है और कुछ ऐसा लौटाता है जो संचायक की तरह दिखता है

// 1. create a concat reducing function that can be passed into `reduce`
const concat = (acc, input) => acc.concat([input])

// note that [1,2,3].reduce(concat, []) would return [1,2,3]

// transforming your reducing function by mapping
// 2. create a generic mapping function that can take a reducing function and return another reducing function
const mapping = (changeInput) => (reducing) => (acc, input) => reducing(acc, changeInput(input))

// 3. create your map function that operates on an input
const getSrc = (x) => x.src
const mappingSrc = mapping(getSrc)

// 4. now we can use our `mapSrc` function to transform our original function `concat` to get another reducing function
const inputSources = [{src:'one.html'}, {src:'two.txt'}, {src:'three.json'}]
inputSources.reduce(mappingSrc(concat), [])
// -> ['one.html', 'two.txt', 'three.json']

// remember this is really essentially just
// inputSources.reduce((acc, x) => acc.concat([x.src]), [])


// transforming your reducing function by filtering
// 5. create a generic filtering function that can take a reducing function and return another reducing function
const filtering = (predicate) => (reducing) => (acc, input) => (predicate(input) ? reducing(acc, input): acc)

// 6. create your filter function that operate on an input
const filterJsonAndLoad = (img) => {
  console.log(img)
  if(img.src.split('.').pop() === 'json') {
    // game.loadSprite(...);
    return false;
  } else {
    return true;
  }
}
const filteringJson = filtering(filterJsonAndLoad)

// 7. notice the type of input and output of these functions
// concat is a reducing function,
// mapSrc transforms and returns a reducing function
// filterJsonAndLoad transforms and returns a reducing function
// these functions that transform reducing functions are "transducers", termed by Rich Hickey
// source: http://clojure.com/blog/2012/05/15/anatomy-of-reducer.html
// we can pass this all into reduce! and without any intermediate arrays

const sources = inputSources.reduce(filteringJson(mappingSrc(concat)), []);
// [ 'one.html', 'two.txt' ]

// ==================================
// 8. BONUS: compose all the functions
// You can decide to create a composing function which takes an infinite number of transducers to
// operate on your reducing function to compose a computed accumulator without ever creating that
// intermediate array
const composeAll = (...args) => (x) => {
  const fns = args
  var i = fns.length
  while (i--) {
    x = fns[i].call(this, x);
  }
  return x
}

const doABunchOfStuff = composeAll(
    filtering((x) => x.src.split('.').pop() !== 'json'),
    mapping((x) => x.src),
    mapping((x) => x.toUpperCase()),
    mapping((x) => x + '!!!')
)

const sources2 = inputSources.reduce(doABunchOfStuff(concat), [])
// ['ONE.HTML!!!', 'TWO.TXT!!!']

संसाधन: अमीर हिक्की ट्रांसड्यूसर पोस्ट


16

यहाँ एक मजेदार समाधान है:

/**
 * Filter-map. Like map, but skips undefined values.
 *
 * @param callback
 */
function fmap(callback) {
    return this.reduce((accum, ...args) => {
        let x = callback(...args);
        if(x !== undefined) {
            accum.push(x);
        }
        return accum;
    }, []);
}

बाइंड ऑपरेटर के साथ प्रयोग करें :

[1,2,-1,3]::fmap(x => x > 0 ? x * 2 : undefined); // [2,4,6]

1
इस विधि ने मुझे अलग map, filterऔर concatकॉल का उपयोग करने से बचाया ।
लॉजिकलब्रांच

11

जवाब शानदार किनारे मामलों:

const thingsWithoutNulls = things.reduce((acc, thing) => {
  if (thing !== null) {
    acc.push(thing);
  }
  return acc;
}, [])

10

सिर्फ एक forEach लूप का उपयोग क्यों न करें?

let arr = ['a', 'b', 'c', 'd', 'e'];
let filtered = [];

arr.forEach(x => {
  if (!x.includes('b')) filtered.push(x);
});

console.log(filtered)   // filtered === ['a','c','d','e'];

या यहां तक ​​कि सरल उपयोग फिल्टर:

const arr = ['a', 'b', 'c', 'd', 'e'];
const filtered = arr.filter(x => !x.includes('b')); // ['a','c','d','e'];

1
सर्वश्रेष्ठ लूप के लिए एक सरल होगा जो फ़िल्टर करता है और एक नई सरणी बनाता है, लेकिन उपयोग mapकरने के संदर्भ के लिए इसे अभी ऐसे ही रहने दें। (था पहले मैं 4yrs इस सवाल पूछा, जब मैं कोडिंग के बारे में कुछ भी नहीं जानते थे)
इस्माइल

पर्याप्त रूप से, यह देखते हुए कि नक्शे के साथ ऊपर का कोई सीधा रास्ता नहीं है और सभी समाधानों ने एक वैकल्पिक पद्धति का उपयोग किया है मुझे लगा कि मैं सबसे सरल तरीके से चिपकेगा जो मैं ऐसा करने के लिए सोच सकता था।
एलेक्स

8
var sources = images.map(function (img) {
    if(img.src.split('.').pop() === "json"){ // if extension is .json
        return null; // skip
    }
    else{
        return img.src;
    }
}).filter(Boolean);

.filter(Boolean)किसी दिए गए सरणी है, जो अपने मामले में है में किसी भी falsey मूल्यों को फ़िल्टर कर देगा null


3

यहां एक उपयोगिता विधि (ES5 संगत) है जो केवल अशक्त मानों को मैप करती है (कम करने के लिए कॉल छिपाता है):

function mapNonNull(arr, cb) {
    return arr.reduce(function (accumulator, value, index, arr) {
        var result = cb.call(null, value, index, arr);
        if (result != null) {
            accumulator.push(result);
        }

        return accumulator;
    }, []);
}

var result = mapNonNull(["a", "b", "c"], function (value) {
    return value === "b" ? null : value; // exclude "b"
});

console.log(result); // ["a", "c"]


1

मैं .forEachइसे पुन: व्यवस्थित करने के लिए उपयोग करता हूं , और resultsफिर परिणाम को सरणी पर धकेलता हूं , फिर इसका उपयोग करता हूं, इस समाधान के साथ मैं सरणी पर दो बार लूप नहीं करूंगा


1

फेलिक्स क्लिंग की टिप्पणी पर चर्चा करने के लिए , आप .filter()इस तरह का उपयोग कर सकते हैं :

var sources = images.map(function (img) {
  if(img.src.split('.').pop() === "json") { // if extension is .json
    return null; // skip
  } else {
    return img.src;
  }
}).filter(Boolean);

यह उस सरणी से फाल्सी वैल्यूज को हटा देगा जो उसके द्वारा लौटाया गया है .map()

आप इसे इस तरह आगे सरल बना सकते हैं:

var sources = images.map(function (img) {
  if(img.src.split('.').pop() !== "json") { // if extension is .json
    return img.src;
  }
}).filter(Boolean);

या यहां तक ​​कि एक तीर-फ़ंक्शन का उपयोग करके एक लाइनर के रूप में, ऑब्जेक्ट विनाशकारी और &&ऑपरेटर:

var sources = images.map(({ src }) => src.split('.').pop() !== "json" && src).filter(Boolean);

0

यहां @theprtk द्वारा प्रदान किए गए कोड का एक अद्यतन संस्करण है । यह एक सामान्यीकृत संस्करण दिखाने के लिए थोड़ा साफ किया जाता है जबकि एक उदाहरण है।

नोट: मैं इसे उनकी पोस्ट के लिए एक टिप्पणी के रूप में जोड़ूंगा लेकिन अभी तक मेरी पर्याप्त प्रतिष्ठा नहीं है

/**
 * @see http://clojure.com/blog/2012/05/15/anatomy-of-reducer.html
 * @description functions that transform reducing functions
 */
const transduce = {
  /** a generic map() that can take a reducing() & return another reducing() */
  map: changeInput => reducing => (acc, input) =>
    reducing(acc, changeInput(input)),
  /** a generic filter() that can take a reducing() & return */
  filter: predicate => reducing => (acc, input) =>
    predicate(input) ? reducing(acc, input) : acc,
  /**
   * a composing() that can take an infinite # transducers to operate on
   *  reducing functions to compose a computed accumulator without ever creating
   *  that intermediate array
   */
  compose: (...args) => x => {
    const fns = args;
    var i = fns.length;
    while (i--) x = fns[i].call(this, x);
    return x;
  },
};

const example = {
  data: [{ src: 'file.html' }, { src: 'file.txt' }, { src: 'file.json' }],
  /** note: `[1,2,3].reduce(concat, [])` -> `[1,2,3]` */
  concat: (acc, input) => acc.concat([input]),
  getSrc: x => x.src,
  filterJson: x => x.src.split('.').pop() !== 'json',
};

/** step 1: create a reducing() that can be passed into `reduce` */
const reduceFn = example.concat;
/** step 2: transforming your reducing function by mapping */
const mapFn = transduce.map(example.getSrc);
/** step 3: create your filter() that operates on an input */
const filterFn = transduce.filter(example.filterJson);
/** step 4: aggregate your transformations */
const composeFn = transduce.compose(
  filterFn,
  mapFn,
  transduce.map(x => x.toUpperCase() + '!'), // new mapping()
);

/**
 * Expected example output
 *  Note: each is wrapped in `example.data.reduce(x, [])`
 *  1: ['file.html', 'file.txt', 'file.json']
 *  2:  ['file.html', 'file.txt']
 *  3: ['FILE.HTML!', 'FILE.TXT!']
 */
const exampleFns = {
  transducers: [
    mapFn(reduceFn),
    filterFn(mapFn(reduceFn)),
    composeFn(reduceFn),
  ],
  raw: [
    (acc, x) => acc.concat([x.src]),
    (acc, x) => acc.concat(x.src.split('.').pop() !== 'json' ? [x.src] : []),
    (acc, x) => acc.concat(x.src.split('.').pop() !== 'json' ? [x.src.toUpperCase() + '!'] : []),
  ],
};
const execExample = (currentValue, index) =>
  console.log('Example ' + index, example.data.reduce(currentValue, []));

exampleFns.raw.forEach(execExample);
exampleFns.transducers.forEach(execExample);

0

आप विधि के बाद उपयोग कर सकते हैं map()filter()आपके मामले में उदाहरण के लिए विधि :

var sources = images.map(function (img) {
  if(img.src.split('.').pop() === "json"){ // if extension is .json
    return null; // skip
  }
  else {
    return img.src;
  }
});

विधि फ़िल्टर:

const sourceFiltered = sources.filter(item => item)

फिर, केवल मौजूदा आइटम नए सरणी में हैं sourceFiltered

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.