对象属性的深度过滤器

时间:2015-02-23 19:55:17

标签: javascript underscore.js

我正在寻找一种能够根据某种数据格式过滤对象属性的方法。说我有这样一个对象:

var data = {
  name : {
    first : "Sam",
    last : "Wise",
    invalid : "Not A Valid Property"
  },
  address : "210 Test St.",
  city : "Springfield",
  state : "CA",
  zip : "65565",
  invalid_key1: "Something invalid",
  invalid_key2: "Another invalid one"
}

理想情况下,我想创建一个方法,深入检查每个属性,只允许过滤对象中存在的键。我有一个适用于浅物体的版本,但是我没有想出一个聪明的方法来为子对象/数组做这个。

var controlObject = {
  'name' : {
    first : true,
    last : true
  },
  'address' : true,
  'city' : true,
  'state' : true,
  'zip' : true
};

filterObj(data, controlObject);
// Would return:
// {
//   name : {
//     first : "Sam",
//     last : "Wise"
//   },
//   address : "210 Test St.",
//   city : "Springfield",
//   state : "CA",
//   zip : "65565"
// }

这是我当前的版本,只检查一个级别。

_.mixin({
    filterObj : function(data, control) {
    // First set some defaults.
    var _data = data || {};
    var _control = control || {};

    // Create arguments to be passed to _.pick()
    var controlKeys = Object.keys(_control);
    var args = [_data].concat(controlKeys);

    // Finally use _.pick() to filter object properties.
    return _.pick.apply(_, args);
  }
});

我试图做一些改进以检查更深层次的对象,但它似乎没有正确地进行。

_.mixin({ 
  filterObj : function(data, control) {
    // First set some defaults.
    var _data = data || {};
    var _control = control || {};

    // Create arguments to be passed to _.pick()
    var controlKeys = Object.keys(_control);
    var args = [_data].concat(controlKeys);

    // Finally use _.pick() to filter object properties.
    var results = _.pick.apply(_, args);

    var moreKeys = Object.keys(_control).filter(function(key) {
      return ( _data[key] == "object" || _data[key] == "array" ) ? true : false
    });

    if( moreKeys.length ) {
      _.each(moreKeys, function(key) {
        results[key] = _.filterObj(_data[key], _control[key]);
      });
      return results;
    } else {
      return results;
    }
  }
});

目前这被设置为Underscore mixin,虽然我没有与这个概念结合(只是让我在整个应用程序中更容易使用)。

3 个答案:

答案 0 :(得分:1)

我知道这个帖子已经两年了,但我的要求与你的要求相似。除了你,我手头没有Lodash / Underscore,所以我提出了一个纯JavaScript解决方案:

function filterObj(data, control) {
  var controlKeys = Object.keys(control),
    results = controlKeys.reduce(function(v, key) {
      if (control[key] === true && data.hasOwnProperty(key)) {
        v[key] = data[key];
      }

      return v;
    }, {});

  controlKeys.filter(function(key) {
    return typeof control[key] === 'object' && control[key] !== null && !Array.isArray(control[key]) && typeof data[key] === 'object' && data[key] !== null && !Array.isArray(data[key]);
  }).forEach(function(key) {
    results[key] = filterObj(data[key], control[key]);
  });

  return results;
}

var data = {
  name: {
    first: 'Sam',
    last: 'Wise',
    invalid: 'Not A Valid Property'
  },
  gender: 'm',
  password: 'abc123',
  location: {
    address: '210 Test St.',
    city: 'Springfield',
    state: 'CA',
    zip: '65565'
  },
  invalid_key1: 'Something invalid',
  invalid_key2: 'Another invalid one'
}

var controlObject = {
  name: {
    first: true,
    last: true
  },
  gender: true,
  password: false,
  location: true
};

console.log(filterObj(data, controlObject));

请注意我改变了初始方法的一些逻辑:

  • 必须将选项标记为true才能过滤掉(严格比较)
  • 可以标记整个(子)对象,以便将其包含在结果

希望这也有助于其他人。

答案 1 :(得分:0)

该行

return ( _data[key] == "object" || _data[key] == "array" ) ? true : false

是你的问题。您错过了typeof运算符,并且js中没有"array"类型 - 所有数组都是对象。我不认为你的函数无论如何都会在数组上按预期工作。更好地使用

return (typeof _data[key] == "object") && (_data[key] !== null) && !_.isArray(_data[key]);

答案 2 :(得分:0)

如果可以的话,请使用Lodash + Deepdash

var filtrate = _.filterDeep(data, function(value, key, parent, ctx) {
  return _.has(controlObject, ctx.path);
});

这是您案件的完整test