2015-01-23 9 views
8

La mia necessità è di fare qualcosa come un _.assign, ma solo se l'oggetto di destinazione ha già la proprietà assegnata. Pensa che gli oggetti sorgente possano avere alcune proprietà da aggiungere, ma anche alcune proprietà che non voglio mescolare._.assegnare solo se la proprietà esiste nell'oggetto di destinazione

Non ho mai usato il meccanismo di callback di _.assign, ma ho provato quanto segue. Ha funzionato, ma ha comunque assegnato la proprietà all'oggetto dest (come non definito). Non voglio che lo assegni affatto.

_.assign(options, defaults, initial, function (destVal, sourceVal) { 
    return typeof destVal == 'undefined' ? undefined : sourceVal; 
}); 

Ho scritto la seguente funzione per fare questo, chiedendomi se il lodash ha già qualcosa in più che è più elegante.

function softMerge (dest, source) { 
    return Object.keys(dest).reduce(function (dest, key) { 
     var sourceVal = source[key]; 

     if (!_.isUndefined(sourceVal)) { 
     dest[key] = sourceVal; 
     } 

     return dest; 
    }, dest); 
} 

risposta

12

Si potrebbe prendere solo le chiavi dal primo oggetto

var firstKeys = _.keys(options); 

Poi prendere un oggetto sottoinsieme dal secondo oggetto, prendendo solo quelle chiavi che esistono sul primo oggetto:

var newDefaults = _.pick(defaults, firstKeys); 

Quindi utilizzare il nuovo oggetto come argomento per _.assign:

_.assign(options, newDefaults); 

O in una sola riga:

_.assign(options, _.pick(defaults, _.keys(options))); 

sembrava funzionare quando ho provato qui: http://jsbin.com/yiyerosabi/1/edit?js,console

+0

sarebbe anche interessante vedere come fare questo con l'_.assign o _merge richiamata e/o utilizzando concatenamento. – sparty02

+1

l'esempio finale manca una parentesi chiusa '_.assign (opzioni, _.pick (valori predefiniti, _.keys (opzioni)));' – seangwright

0

Ecco una versione profonda immutabile, mi chiamano "unione che mantiene la forma", a macchina che utilizza lodash:

function _mergeKeepShapeArray(dest: Array<any>, source: Array<any>) { 
    if (source.length != dest.length) { 
     return dest; 
    } 
    let ret = []; 
    dest.forEach((v, i) => { 
     ret[i] = _mergeKeepShape(v, source[i]); 
    }); 
    return ret; 
} 

function _mergeKeepShapeObject(dest: Object, source: Object) { 
    let ret = {}; 
    Object.keys(dest).forEach((key) => { 
     let sourceValue = source[key]; 
     if (typeof sourceValue !== "undefined") { 
      ret[key] = _mergeKeepShape(dest[key], sourceValue); 
     } else { 
      ret[key] = dest[key]; 
     } 
    }); 
    return ret; 
} 

function _mergeKeepShape(dest, source) { 
    // else if order matters here, because _.isObject is true for arrays also 
    if (_.isArray(dest)) { 
     if (!_.isArray(source)) { 
      return dest; 
     } 
     return _mergeKeepShapeArray(dest, source); 
    } else if (_.isObject(source)) { 
     if (!_.isObject(source)) { 
      return dest; 
     } 
     return _mergeKeepShapeObject(dest, source); 
    } else { 
     return source; 
    } 
} 

/** 
* Immutable merge that retains the shape of the `existingValue` 
*/ 
export const mergeKeepShape = <T>(existingValue: T, extendingValue): T => { 
    return _mergeKeepShape(existingValue, extendingValue); 
} 

E un semplice test per vedere come mi visione così fusione dovrebbe funzionare:

let newObject = mergeKeepShape(
    { 
     a : 5, 
     // b is not here 
     c : 33, 
     d : { 
      e : 5, 
      // f is not here 
      g : [1,1,1], 
      h : [2,2,2], 
      i : [4,4,4], 
     } 
    }, 
    { 
     a : 123, 
     b : 444, 
     // c is not here 
     d : { 
      e : 321, 
      f : 432, 
      // g is not here 
      h : [3,3,3], 
      i : [1,2], 
     } 
    } 
); 
expect(newObject).toEqual({ 
    a : 123, 
    // b is not here 
    c : 33, 
    d : { 
     e : 321, 
     // f is not here, 
     g : [1,1,1], 
     h : [3,3,3], 
     i : [4,4,4] 
    } 
}); 

Ho usato io stesso senza immutazioni nel test, ma non ho visto la necessità di inserirlo in questa risposta.

Con la presente inserisco questo nel pubblico dominio.

0

Un altro modo per ottenere questo risultato è combinando _.mapObject con _.has

_.mapObject(object1, function(v, k) { 
    return _.has(object2, k) ? object2[k] : v; 
}); 

Spiegazione:

  1. Traverse tutti chiave/valore coppie di object1 utilizzando _.mapObject
  2. Utilizzando _.has, controllare se nome proprietà k esiste anche in object2.
  3. In caso affermativo, copiare il valore assegnato al tasto object2 di k su object1, altrimenti restituire il valore esistente dell'oggetto1 (v).

Plunkr

Problemi correlati