javascript - Kuinka ohittaa elementti .map():ssa?
Kuinka voin ohittaa taulukon alkion .map-metodissa?
Koodini:
var sources = images.map(function (img) {
if(img.src.split('.').pop() === "json"){ // if extension is .json
return null; // skip
}
else{
return img.src;
}
});
Tämä palauttaa:
["img.png", null, "img.png"]
Vastaus #1
Suodata se ensin .filter()-metodilla:
var sources = images.filter(function(img) {
if (img.src.split('.').pop() === "json") {
return false; // skip
}
return true;
}).map(function(img) { return img.src; });
Jos et halua tehdä sitä, mikä ei ole kohtuutonta, koska sillä on jonkin verran kustannuksia, voit käyttää yleisempää .reduce()-metodia. Voit yleisesti ilmaista .map()-metodin .reduce-metodin avulla:
someArray.map(function(element) {
return transform(element);
});
voidaan kirjoittaa muodossa
someArray.reduce(function(result, element) {
result.push(transform(element));
return result;
}, []);
Joten jos sinun tarvitsee ohittaa elementtejä, voit tehdä sen helposti .reduce()-menetelmällä:
var sources = images.reduce(function(result, img) {
if (img.src.split('.').pop() !== "json") {
result.push(img.src);
}
return result;
}, []);
Tässä versiossa ensimmäisen esimerkin .filter()-koodin osa on osa .reduce()-paluuarvokutsua. Kuvan lähde lisätään tulostaulukkoon vain siinä tapauksessa, että suodatinoperaatio olisi säilyttänyt sen.
päivitys — Tämä kysymys saa paljon huomiota, ja haluaisin lisätä seuraavan selventävän huomautuksen. .map()-funktion tarkoitus käsitteenä on tehdä juuri sitä, mitä "map" tarkoittaa: muuntaa arvolista toiseen arvolistaan tiettyjen sääntöjen mukaan. Aivan kuten paperikartta jostain maasta näyttäisi oudolta, jos pari kaupunkia puuttuisi kokonaan, luettelosta toiseen luetteloon tehtävä kartoitus on järkevää vain, kun tulosarvoilla on 1:1-suhde.
En sano, etteikö olisi järkevää luoda uutta listaa vanhasta listasta, joitakin arvoja poissulkien. Yritän vain tehdä selväksi, että .map():llä on yksi yksinkertainen tarkoitus, joka on luoda uusi taulukko samanpituisena kuin vanha taulukko, mutta arvoilla, jotka on muodostettu vanhojen arvojen muuntamisen kautta.
Vastaus #2
Vuodesta 2019 lähtien Array.prototype.flatMap on hyvä vaihtoehto.
images.flatMap(({src}) => src.endsWith('.json') ? [] : src);
MDN:ltä:
flatMap-funktiota voidaan käyttää tapana lisätä ja poistaa kohteita (muuttaa kohteiden määrää) mapin aikana. Toisin sanoen se mahdollistaa monen kohteen kartoittamisen moniksi kohteiksi (käsittelemällä kutakin syötekohdetta erikseen), sen sijaan että se olisi aina yhden suhde yhteen. Tässä mielessä se toimii filtrin vastakohdaksi. Palauta yksielementtinen taulukko säilyttääksesi kohteen, monielementtinen taulukko lisätäksesi kohteita tai nollaelementtinen taulukko poistaaksesi kohteen.
Vastaus #3
Mielestäni yksinkertaisin tapa ohittaa joitakin taulukon elementtejä on käyttää filter()-metodia.
Käyttämällä tätä menetelmää (ES5) ja ES6-syntaksia voit kirjoittaa koodisi yhteen riviin, ja tämä palauttaa sen, mitä haluat:
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);Vastaus #4
Tiivistelmä: Voit ensin suodattaa taulukon ja sitten suorittaa mapin, mutta tämä vaatisi kaksi läpikäyntiä taulukosta (filter palauttaa taulukon mapille). Koska tämä taulukko on pieni, suorituskyvyn kustannus on hyvin pieni. Voit myös käyttää yksinkertaista reducea. Kuitenkin, jos haluat keksiä uudelleen, miten tämä voidaan tehdä yhdellä läpikäynnillä taulukosta (tai mistä tahansa tietotyypistä), voit käyttää ideaa nimeltä "transducers", jonka teki suosituksi Rich Hickey.
Vastaus:
Meidän ei pitäisi vaatia pisteketjun lisäämistä ja taulukon [].map(fn1).filter(f2)... käsittelyä, koska tämä lähestymistapa luo väliin tulevia taulukoita muistiin jokaisessa reducing-funktiossa.
Paras lähestymistapa toimii itse vähentämisfunktion avulla, joten data kulkee vain yhden kerran eikä ylimääräisiä taulukoita tarvita.
Reducing-funktio on funktio, joka annetaan reduce-funktiolle ja ottaa akkumulatorin ja lähteen syötteen ja palauttaa jotain, joka näyttää akkumulatorilta
// 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!!!']
Resurssit: Rich Hickeyn transducer-postaus
Vastaus #5
Tässä on hauska ratkaisu:
/**
* Filter-map. Like map, but skips undefined values.
*
* @param callback
*/
function fmap(callback) {
return this.reduce((accum, ...args) => {
const x = callback(...args);
if(x !== undefined) {
accum.push(x);
}
return accum;
}, []);
}
Käytä sitomisoperaattorin kanssa:
[1,2,-1,3]::fmap(x => x > 0 ? x * 2 : undefined); // [2,4,6]
Vastaus #6
Miksi ei vain käytettäisi forEach-silmukkaa?
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'];Tai jopa yksinkertaisemmin käytä suodatinta:
const arr = ['a', 'b', 'c', 'd', 'e'];
const filtered = arr.filter(x => !x.includes('b')); // ['a','c','d','e'];
Vastaus #7
Vastaa ilman tarpeettomia reunatapauksia:
const thingsWithoutNulls = things.reduce((acc, thing) => {
if (thing !== null) {
acc.push(thing);
}
return acc;
}, [])
Vastaus #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) suodattaa pois kaikki epätoden arvot annetusta taulukosta, joka sinun tapauksessasi on null.
Vastaus #9
Laajentaaksesi Felix Klingin kommenttia, voit käyttää .filter() näin:
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);
Se poistaa epätotelliset arvot taulukosta, jonka .map() palauttaa
Voisit yksinkertaistaa sen edelleen näin:
var sources = images.map(function (img) {
if(img.src.split('.').pop() !== "json") { // if extension is .json
return img.src;
}
}).filter(Boolean);
Tai jopa yhden rivin ratkaisuna käyttämällä nuolifunktiota, olion purkua ja &&-operaattoria:
var sources = images.map(({ src }) => src.split('.').pop() !== "json" && src).filter(Boolean);
