Глубокий клон без полей

Позвольте мне иметь следующий объект javascript. Теперь я хочу клонировать его, но без некоторых полей. Например, я хочу клонированный объект без полей "lastName" и "cars.age"
Ввод

{
   "firstName":"Fred",
   "lastName":"McDonald",
      "cars":[
           {
              "type":"mersedes",
              "age":5
           },
           {
              "model":"bmw",
              "age":10
           }
       ]
}  

Выход (клонированный)

{
   "firstName":"Fred",
   "cars":[
       {
          "model":"mersedes"
       },
       {
          "model":"bmw"
       }
   ]
}   

Я могу сделать что-то вроде

var human = myJson   
var clone = $.extend(true, {}, human)  
delete clone.lastName  
_.each(clone.cars, function(car))  
{  
   delete car.age  
}  

Знаете ли вы более легкое решение?

Ответ 1

Если вы не возражаете против добавления прототипов объектов, это простое решение. Вы можете изменить его для своего использования.

Object.prototype.deepOmit = function(blackList) {
  if (!_.isArray(blackList)) { 
    throw new Error("deepOmit(): argument must be an Array");
  }

  var copy = _.omit(this, blackList);
  _.each(blackList, function(arg) {
    if (_.contains(arg, '.')) {
      var key  = _.first(arg.split('.'));
      var last = arg.split('.').slice(1);
      copy[key] = copy[key].deepOmit(last);
    }
  });
  return copy;
};

Array.prototype.deepOmit = function(blackList) {
  if (!_.isArray(blackList)) { 
    throw new Error("deepOmit(): argument must be an Array");
  }

  return _.map(this, function(item) {
    return item.deepOmit(blackList);
  });
};

Тогда, когда у вас есть такой объект:

var personThatOwnsCars = {
   "firstName":"Fred",
   "lastName":"McDonald",
      "cars":[
           {
              "type":"mersedes",
              "age":5
           },
           {
              "model":"bmw",
              "age":10
           }
       ]
};

Вы можете делать магию как это.

personThatOwnsCars.deepOmit(["firstName", "cars.age"]);

Или даже магия такая!

[person1, person2].deepOmit(["firstName", "cars.age"]);

Ответ 2

Вот отдельная функция, зависящая от lodash/underscore, которую я написал, что делает то же самое.

Он вызывает обратный вызов для каждой пары (value, indexOrKey) в объекте или массиве, и если true, эта пара будет отсутствовать в результирующем объекте.

Обратный вызов вызывается после того, как значение было посещено, поэтому вы можете опустить целое поддерево значений, которые соответствуют вашему условию.

function deepOmit(sourceObj, callback, thisArg) {
    var destObj, i, shouldOmit, newValue;

    if (_.isUndefined(sourceObj)) {
        return undefined;
    }

    callback = thisArg ? _.bind(callback, thisArg) : callback;

    if (_.isPlainObject(sourceObj)) {
        destObj = {};
        _.forOwn(sourceObj, function(value, key) {
            newValue = deepOmit(value, callback);
            shouldOmit = callback(newValue, key);
            if (!shouldOmit) {
                destObj[key] = newValue;
            }
        });
    } else if (_.isArray(sourceObj)) {
        destObj = [];
        for (i = 0; i <sourceObj.length; i++) {
            newValue = deepOmit(sourceObj[i], callback);
            shouldOmit = callback(newValue, i);
            if (!shouldOmit) {
                destObj.push(newValue);
            }
        }
    } else {
        return sourceObj;
    }

    return destObj;
}

Некоторые примеры

var sourceObj = {
    a1: [ undefined, {}, { o: undefined } ],
    a2: [ 1, undefined ],
    o: { s: 's' } 
};

deepOmit(sourceObj, function (value) {
    return value === undefined;
});
//=> { a1: [ {}, {} ], a2: [ 1 ], o: { s: 's' }}

//omit empty objects and arrays too
deepOmit(sourceObj, function (value) {
    return value === undefined ||
        (_.isPlainObject(value) && !_.keys(value).length) ||
        (_.isArray(value) && !value.length);
});
//=> { a2: [ 1 ], o: { s: 's' }}

//indexOrKey is the string key or the numeric index if the object is array
deepOmit([ 0, 1, 2, 3, 4 ], function (value, indexOrKey) {
    return indexOrKey % 2;
});
//=> [ 0, 2, 4 ]