883 Stimmen

Wie überspringt man ein Element in .map()?

Wie kann ich ein Array-Element in .map überspringen?

Mein Code:

var sources = images.map(function (img) {
    if(img.src.split('.').pop() === "json"){ // wenn die Erweiterung .json ist
        return null; // überspringen
    }
    else{
        return img.src;
    }
});

Dies wird zurückgeben:

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

1248voto

Pointy Punkte 387467

Einfach zuerst .filter() anwenden:

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

Wenn Sie das nicht tun möchten, was nicht unvernünftig ist, da es etwas kostet, können Sie das allgemeinere .reduce() verwenden. Sie können .map() im Allgemeinen in Bezug auf .reduce() ausdrücken:

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

kann geschrieben werden als

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

Also, wenn Sie Elemente überspringen müssen, können Sie das leicht mit .reduce() tun:

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

In dieser Version ist der Code im .filter() aus dem ersten Beispiel Teil des .reduce()-Callbacks. Die Bildquelle wird nur dann in das Ergebnisarray eingefügt, wenn der Filtervorgang es beibehalten hätte.

Update - Diese Frage erhält viel Aufmerksamkeit, und ich möchte die folgende erläuternde Bemerkung hinzufügen. Der Zweck von .map() als Konzept besteht darin, genau das zu tun, was "map" bedeutet: eine Liste von Werten gemäß bestimmter Regeln in eine andere Liste von Werten umzuwandeln. Genau wie eine Landkarte eines Landes seltsam erscheinen würde, wenn ein paar Städte komplett fehlen würden, macht eine Zuordnung von einer Liste zu einer anderen nur dann wirklich Sinn, wenn es eine 1-zu-1-Sammlung von Ergebniswerten gibt.

Ich sage nicht, dass es keinen Sinn macht, eine neue Liste aus einer alten Liste mit einigen ausgeschlossenen Werten zu erstellen. Ich versuche nur klarzustellen, dass .map() eine einzige einfache Absicht hat, nämlich ein neues Array der gleichen Länge wie ein altes Array zu erstellen, nur mit Werten, die durch eine Transformation der alten Werte gebildet wurden.

354voto

Trevor Dixon Punkte 19874

Seit 2019 ist Array.prototype.flatMap eine gute Option.

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

Von MDN:

flatMap kann als Möglichkeit genutzt werden, um Elemente hinzuzufügen und zu entfernen (die Anzahl der Elemente zu ändern) während der Verwendung von map. Mit anderen Worten, es ermöglicht Ihnen, viele Elemente auf viele Elemente abzubilden (indem jedes Eingabeelement einzeln verarbeitet wird), anstatt immer eins-zu-eins. In diesem Sinne funktioniert es wie das Gegenteil von filter. Geben Sie einfach ein Array mit einem Element zurück, um das Element zu behalten, ein Array mit mehreren Elementen, um Elemente hinzuzufügen, oder ein Array mit null Elementen, um das Element zu entfernen.

41voto

simhumileco Punkte 26451

Ich denke, der einfachste Weg, einige Elemente aus einem Array zu überspringen, besteht darin, die filter()-Methode zu verwenden.

Indem Sie diese Methode (ES5) und die ES6-Syntax verwenden, können Sie Ihren Code in einer Zeile schreiben, und das wird das zurückgeben, was Sie wollen:

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);

27voto

theptrk Punkte 730

Zusammenfassung: Sie können zuerst Ihr Array filtern und dann Ihre Abbildung durchführen, aber dies erfordert zwei Durchläufe des Arrays (Filter gibt ein Array zurück, um zu kartieren). Da dieses Array klein ist, ist der Leistungsaufwand sehr gering. Sie können auch ein einfaches Reduzieren durchführen. Wenn Sie jedoch neu überlegen möchten, wie dies mit einem einzigen Durchlauf über das Array (oder einen beliebigen Datentyp) gemacht werden kann, können Sie eine Idee namens "Transducers" verwenden, die von Rich Hickey populär gemacht wurde.

Antwort:

Wir sollten nicht verlangen, die Punktketten zu erhöhen und auf dem Array [].map(fn1).filter(f2)... zu arbeiten, da dieser Ansatz Zwischenarrays im Speicher für jede Reduzierungsfunktion erstellt.

Der beste Ansatz basiert auf der eigentlichen Reduzierungsfunktion, sodass nur ein Daten-Durchlauf durchgeführt wird und keine zusätzlichen Arrays vorhanden sind.

Die Reduzierungsfunktion ist die Funktion, die an reduce übergeben wird und einen Akkumulator und Eingaben aus der Quelle annimmt und etwas zurückgibt, das wie der Akkumulator aussieht.

// 1. Erstellen Sie eine Verkettungsreduktionsfunktion, die in `reduce` übergeben werden kann
const concat = (acc, input) => acc.concat([input])

// Beachten Sie, dass [1,2,3].reduce(concat, []) [1,2,3] zurückgeben würde

// Ihr Reduzierungsfunktion durch kartieren transformieren
// 2. Erstellen Sie eine generische Kartierungsfunktion, die eine Reduzierungsfunktion einnehmen und eine andere Reduzierungsfunktion zurückgeben kann
const mapping = (changeInput) => (reduzieren) => (acc, input) => reduzieren(acc, changeInput(input))

// 3. Erstellen Sie Ihre Kartierungsfunktion, die auf einen Eingang wirkt
const getSrc = (x) => x.src
const mappingSrc = mapping(getSrc)

// 4. Jetzt können wir unsere `mapSrc`-Funktion verwenden, um unsere ursprüngliche Funktion `concat` zu transformieren und eine andere Reduzierungsfunktion zu erhalten
const inputSources = [{src:'one.html'}, {src:'two.txt'}, {src:'three.json'}]
inputSources.reduce(mappingSrc(concat), [])
// -> ['one.html', 'two.txt', 'three.json']

// Denken Sie daran, dass dies im Grunde genommen nur
// inputSources.reduce((acc, x) => acc.concat([x.src]), [])

// Ihre Reduzierungsfunktion durch Filtern transformieren
// 5. Erstellen Sie eine generische Filterungsfunktion, die eine Reduzierungsfunktion einnehmen und eine andere Reduzierungsfunktion zurückgeben kann
const filtering = (Prädikat) => (reduzieren) => (acc, input) => (Prädikat(input) ? reduzieren(acc, input): acc)

// 6. Erstellen Sie Ihre Filterfunktion, die auf einen Eingang wirken
const filterJsonAndLoad = (img) => {
  console.log(img)
  if(img.src.split('.').pop() === 'json') {
    // game.loadSprite(...);
    return false;
  } else {
    return true;
  }
}
const filteringJson = filtering(filterJsonAndLoad)

// 7. Beachten Sie den Typ des Eingangs und des Ausgangs dieser Funktionen
// concat ist eine Reduzierungsfunktion,
// mapSrc transformiert und gibt eine Reduzierungsfunktion zurück
// filterJsonAndLoad transformiert und gibt eine Reduzierungsfunktion zurück
// Diese Funktionen, die Reduzierungsfunktionen transformieren, werden als "Transducers" bezeichnet, von Rich Hickey benannt
// Quelle: http://clojure.com/blog/2012/05/15/anatomy-of-reducer.html
// Wir können dies alles in reduce! geben und ohne Zwischenarrays auskommen

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

// ==================================
// 8. BONUS: Alle Funktionen zusammenfügen
// Sie können beschließen, eine zusammensetzende Funktion zu erstellen, die eine unendliche Anzahl von Transducers aufnimmt, um auf Ihre Reduzierungsfunktion zu wirken und einen berechneten Akkumulator ohne jemals dieses Zwischenarray zu erstellen, zusammenzusetzen
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!!!']

Ressourcen: Reicher Hickey Transducers Beitrag

25voto

mpen Punkte 253762

Hier ist eine lustige Lösung:

/**
 * Filter-Map. Wie map, aber überspringt undefinierte Werte.
 *
 * @param callback
 */
function fmap(callback) {
    return this.reduce((accum, ...args) => {
        const x = callback(...args);
        if(x !== undefined) {
            accum.push(x);
        }
        return accum;
    }, []);
}

Verwenden Sie den Bind Operator:

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

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X