Gli effetti collaterali in "ogni" o "cattivo" di Array?

9

Mi è sempre stato insegnato che avere effetti collaterali in una condizione di if non è buona. Quello che voglio dire è

if (conditionThenHandle()) {
    // do effectively nothing
}

... al contrario di

if (condition()) {
    handle();
}

... e lo capisco, e i miei colleghi sono felici perché non lo faccio, e andiamo tutti a casa alle 17:00 di venerdì e tutti hanno un buon fine settimana.

Ora, ECMAScript5 ha introdotto metodi come every() e some() in Array , e li trovo molto utili. Sono più puliti di for (;;;) , ti offrono un altro ambito, e rendono l'elemento accessibile da una variabile.

Tuttavia, durante la convalida dell'input, mi capita più spesso di utilizzare every / some nella condizione per convalidare l'input, quindi utilizzare every / some di nuovo nel corpo per convertire l'input in un modello utilizzabile;

if (input.every(function (that) {
    return typeof that === "number";
})) {
    input.every(function (that) {
        // Model.findById(that); etc
    }
} else {
    return;
}

... quando ciò che voglio fare è

.

if (!input.every(function (that) {
    var res = typeof that === "number";

    if (res) {
        // Model.findById(that); etc.
    }

    return res;
})) {
    return;
}

... che mi dà effetti collaterali in una if , condizione, che è male.

In confronto, questo sarebbe il codice con un vecchio for (;;;) ;

for (var i=0;i<input.length;i++) {
    var curr = input[i];

    if (typeof curr === "number") {
        return;
    }

    // Model.findById(curr); etc.
}

Le mie domande sono:

  1. È decisamente una cattiva pratica?
  2. Sono I (mis | ab) usando some e every ( dovrei sto usando un for(;;;) per questo?)
  3. C'è un approccio migliore?
posta Isaac 09.02.2013 - 21:17
fonte

2 risposte

7

Se comprendo correttamente il tuo punto, sembra che tu stia usando male o abusando di every e some , ma è un po 'inevitabile se vuoi modificare direttamente gli elementi degli array. Correggimi se ho torto, ma quello che stai cercando di fare è scoprire se alcuni o tutti gli elementi della sequenza mostrano una certa condizione, quindi modificare quegli elementi. Inoltre, il tuo codice sembra applicare qualcosa a tutti gli elementi finché non ne trovi uno che non trasmette il predicato e non penso che sia ciò che intendi fare. In ogni modo.

Prendiamo il tuo primo esempio (leggermente modificato)

if (input.every(function (that) {
    return typeof that === "number";
})) {
    input.every(function (that) {
        that.foo();
    }
} else {
    return;
}

Quello che stai facendo qui in realtà va un po 'contro lo spirito dei concetti some / every / map / reduce / filter / etc. Every non è pensato per essere usato per influenzare ogni elemento che è conforme a qualcosa, piuttosto dovrebbe essere usato solo per dirti se ogni oggetto in una collezione lo fa. Se si desidera applicare una funzione a tutti gli elementi per i quali un predicato viene valutato come true, il "buon" modo per farlo è

var filtered = array.filter(function(item) {
    return typeof item === "number";
});

var mapped = filtered.map(function(item) {
    return item.foo(); //provided foo() has no side effects and returns a new object of item's type instead.  See note about foreach below.
});

In alternativa, potresti utilizzare foreach anziché map per modificare gli elementi sul posto.

La stessa logica si applica a some , in pratica:

  • Si utilizza every per verificare se tutti gli elementi in un array superano un test.
  • Si utilizza some per verificare se almeno un elemento in un array ne supera alcuni test.
  • Si utilizza map per restituire un nuovo array contenente 1 elemento (che è il risultato di una funzione di tua scelta) per ogni elemento in un matrice di input.
  • Si utilizza filter per restituire una matrice di lunghezza 0 < length < initial array length elementi, tutti contenuti nell'array originale e tutti superando il test del predicato fornito.
  • Si utilizza foreach se si desidera una mappa ma sul posto
  • Si utilizza reduce se si desidera combinare i risultati di una matrice in un risultato a oggetto singolo (che potrebbe essere una matrice ma non deve).

Più le usi (e più scrivi il codice LISP), più ti rendi conto di come sono correlate e di come è possibile emularle / implementarle una con le altre. Ciò che è potente con queste query e ciò che è davvero interessante è la loro semantica, e come ti spingono davvero ad eliminare gli effetti collaterali dannosi nel tuo codice.

EDIT (alla luce dei commenti): Quindi supponiamo di voler convalidare che ogni elemento è un oggetto e convertirli in un modello di applicazione se sono tutti validi. Un modo per farlo in un singolo passaggio sarebbe:

var dirty = false;
var app_domain_objects = input.map(function(item) {
    if(validate(item)) {
        return new Model(item);
    } else {
        dirty = true; //dirty is captured by the function passed to map, but you know that :)
    }
});
if(dirty) {
    //your validation test failed, do w/e you need to
} else {
    //You can use app_domain_objects
}

In questo modo, quando un oggetto non supera la convalida, continui a continuare l'iterazione attraverso l'intero array, che sarebbe più lento della semplice convalida con every . Tuttavia, la maggior parte delle volte il tuo array sarà valido (o dovrei sperarlo), quindi nella maggior parte dei casi eseguirai un singolo passaggio sull'array e finirai con un array utilizzabile di oggetti del modello di applicazione. La semantica sarà rispettata, gli effetti collaterali evitati e tutti saranno felici!

Si noti che è anche possibile scrivere la propria query, simile a foreach, che applicherà una funzione a tutti i membri di un array e restituirà true / false se tutti superano un test di predicato. Qualcosa come:

function apply_to_every(arr, predicate, func) {
    var passed = true;
    for(var i = 0; i < array.length; ++i) {
        if(predicate(arr[i])) {
            func(arr[i]);
        } else {
            passed = false;
            break;
        }
    }
    return passed;
}

Anche se ciò dovrebbe modificare la matrice sul posto.

Spero che questo aiuti, è stato molto divertente scrivere. Cheers!

    
risposta data 10.02.2013 - 06:15
fonte
-1

Gli effetti collaterali non sono nella condizione if, sono nel corpo di if. Hai solo determinato se eseguire o meno quel corpo nella condizione attuale. Non c'è niente di sbagliato nel tuo approccio qui.

    
risposta data 09.02.2013 - 21:25
fonte

Leggi altre domande sui tag