【问题标题】:Keeping only certain properties in a JavaScript object仅保留 JavaScript 对象中的某些属性
【发布时间】:2014-04-07 19:21:54
【问题描述】:

我有一个对象。我想通过删除除某些特定属性之外的所有属性来修改对象(而不是克隆它)。例如,如果我从这个对象开始:

var myObj={
    p1:123,
    p2:321,
    p3:{p3_1:1231,p3_2:342},
    p4:'23423',
    //....
    p99:{p99_1:'sadf',p99_2:234},
    p100:3434
}

只需要属性p1、p2和p100,我怎样才能获得这个对象:

var myObj={
    p1:123,
    p2:321,
    p100:3434
}

我了解如何通过蛮力做到这一点,但想要一个更优雅的解决方案。

【问题讨论】:

  • 如果不使用蛮力(或克隆对象),我认为这是不可能的
  • 什么是“蛮力”?
  • 我猜你玩得很开心用户,有很多东西要读;)
  • 这个人首先给出了最好的答案之一(第二个代码块):stackoverflow.com/a/22202827/1636522。然而,正如George Jempty 所提到的,旧的浏览器不会喜欢indexOf。不过可以在此处找到解决方法:mdn.beonex.com/en/Core_JavaScript_1.5_Reference/Global_Objects/….
  • @Ryan 不错的答案。 let result = (({ p1, p2, p100 }) => ({ p1, p2, p100 }))(myObj); 工作完美。不确定切换我选择的答案是否为时已晚,但如果您希望将此作为答案发布,请尝试。谢谢

标签: javascript


【解决方案1】:

只需重新初始化对象:

myObj = {
    p1:   myObj.p1,
    p2:   myObj.p2,
    p100: myObj.p100
};

另一种方法是删除某些属性,效果较差:

var prop = ['p1', 'p2', 'p100'];
for (var k in myObj) {
    if (prop.indexOf(k) < 0) {
        delete myObj[k];
    }
}

【讨论】:

  • @JuanMendes 它不会克隆它。
  • 但是你仍然没有你开始使用的对象
  • 第二个代码块有效,与第一个不同。事实上,如果另一个变量引用myObj 怎么办?
  • @VisioN 对第一个对象的任何其他引用呢?
  • 属性数量不是问题@user1032531,关注这句话:“如果你不需要保留对原始对象的引用”。给我一分钟,我会发布一个答案来向你解释。
【解决方案2】:
var myObj = {a: 1, b: 2, c:3};

function keepProps(obj, keep) {
    for (var prop in obj) {
        if (keep.indexOf( prop ) == -1) {
            delete obj[prop];
        }             
    }
}

keepProps(myObj, ['a', 'b']);
console.log(myObj);

http://jsfiddle.net/mendesjuan/d8Sp3/2/

【讨论】:

  • 在我看来是蛮力的,他不想这样
  • @tewathia 请详细说明“蛮力”。根据 OP 的问题,这是最动态的方法。
  • 好吧,你正在遍历对象的所有属性(我同意这确实是在不克隆的情况下获得所需对象的最佳方法),我相信这就是他所说的“蛮力” "
  • 为什么要使用2个嵌套循环和indexOf
  • @GeorgeJempty 你太小气了 ;) My answer 顺便说一下是跨浏览器兼容的:D
【解决方案3】:

你可以使用delete:

for (var k in myObj) {
    if (k !== 'p1' && k !== 'p2' && k !== 'p100') {
        delete myObj[k];
    }
}

indexOf 的替代方案:

var take = /^p(1|2|100)$/;
for (var k in myObj) {
    if (!take.test(k)) {
        delete myObj[k];
    }
}

更短:

var take = /^p(1|2|100)$/;
for (var k in myObj) {
    take.test(k) || delete myObj[k];
}

数组到正则表达式:

var take = [1, 2, 100];
take = new RegExp('^p(' + take.join('|') + ')$'); // /^p(1|2|100)$/
take.test('p1'); // true
take.test('p3'); // false

在函数中有用:

function take(o, ids) {
    var take = new RegExp('^p(' + ids.join('|') + ')$');
    for (var k in o) take.test(k) || delete o[k];
    return o;
}

用法:

take(myObj, [1, 2, 100]); // { p1: 123, p2: 321, p100: 3434 }

如果你不喜欢正则表达式:

function take(o, keys) {
    for (var k in o) contains(keys, k) || delete o[k];
    return o;
}

function contains(array, value) {
    var i = -1, l = array.length;
    while (++i < l) if (array[i] === value) return true;
    return false;
}

function prefix(array, prefix) {
    var i = -1, l = array.length, output = [];
    while (++i < l) output.push(prefix + array[i]);
    return output;
}

用法:

take(myObj, ['p1', 'p2', 'p100']);
// with a middleman :
var idsToTake = [1, 2, 100];
take(myObj, prefix(idsToTake, 'p'));

【讨论】:

    【解决方案4】:

    您可以在您的第一个对象上创建一个视图,这是一种只保留所需属性的代理。
    例如,以下函数将创建一个允许读取和写入底层对象的视图,只保留选择的属性。
    您只需删除 setter 即可非常轻松地将其设为只读。
    您可能还想密封代理对象,以便我以后无法对其进行修改。

    function createView(obj, propList) {
        var proxy = {};     
        for (var propIndex in propList) {    
             var prop=propList[propIndex];
             Object.defineProperty(proxy, prop, 
                    {  enumerable : true , 
                       get : getter.bind(obj,prop), 
                       set : setter.bind(obj,prop)   } );
    
        }    
      return proxy;
    }
    
    function getter(prop) { return this[prop] ; }
    
    function setter(prop, value) { return this[prop] = value ; }
    

    一个使用示例是:

    var myObj={
        p1:123,
        p2:321,
        p3:{p3_1:1231,p3_2:342},
        p4:'23423',
        p99:{p99_1:'sadf',p99_2:234},
        p100:3434
    };
    
    var objView = createView(myObj, ['p1', 'p2', 'p100']);
    

    在这里,objView“反映”了 myObj 的所需属性。 你可以看看我这里做的小jsbin:

    http://jsbin.com/munudewa/1/edit?js,console

    结果:

    "on objView, p1:123 p2:321 p100:3434 and p4 (not in view) is : undefined"
    "modifiying, on the view,  p1 to 1000 and p2 to hello "
    "on objView, p1:1000 p2:hello p100:3434 and p4 (not in view) is : undefined"
    "modifiying, on the viewed object,  p1 to 200 and p2 to bye "
    "on objView, p1:200 p2:bye p100:3434 and p4 (not in view) is : undefined"
    

    请注意:
    1) 您可以通过其视图覆盖对象,只保留所需的属性。
    2)您可以将原始对象保存在隐藏属性/闭包中,以便以后更改您公开的属性。

    【讨论】:

      【解决方案5】:

      我想你可以在原型中添加一个新方法:

      if (!('keepOnlyTheseProps' in Object.prototype)) {
        Object.prototype.keepOnlyTheseProps = function (arr) {
          var keys = Object.keys(this);
          for (var i = 0, l = keys.length; i < l; i++) {
            if (arr.indexOf(keys[i]) < 0) delete this[keys[i]];
          }
        }
      }
      
      myObj.keepOnlyTheseProps(['p1', 'p2', 'p100']);
      

      Fiddle.

      【讨论】:

        【解决方案6】:

        将白名单键的映射传递给 IIFE(立即调用的函数表达式);不仅优雅而且灵活的 IMO(特别是如果转移到与胡安·门德斯的回答不同的功能)

        var myObj={
            p1:123,
            p2:321,
            p3:{p3_1:1231,p3_2:342},
            p4:'23423',
            //....
            p99:{p99_1:'sadf',p99_2:234},
            p100:3434
        }
        
        var myObj = (function(origObj, whiteListMap) {
            for (var prop in origObj) {
                if (!whiteListMap[prop]) {
                    delete origObj[prop];
                }
            }
            return myObj;
        })(myObj, {'p1': 1, 'p2': 1, 'p100': 1});
        
        console.log(JSON.stringify(myObj)); //{"p1":123,"p2":321,"p100":3434}
        

        【讨论】:

        • 如果列入白名单的属性的值为 null、0、'' 或 undefined,它将不在构建对象中。
        • @GameAlchemist,这就是为什么我总是将它们设置为 1。另一种方法是传入一个白名单数组,然后从中创建一个对象;这对用户来说更容易一些。我不同意在数组上使用 indexOf ;即使在本机它也需要循环而不是简单的哈希查找
        • 您可以使用 'in' 来避免必须设置为 1 : if (prop in whiteListMap)。
        【解决方案7】:

        存储在名为 o 的变量中的对象:

        var o = { a: 1, b: 2 };
        

        对该对象的新引用:

        var p = o;
        

        op 都指向同一个对象:

        o // { a: 1, b: 2 }
        p // { a: 1, b: 2 }
        o === p // true
        

        让我们通过o更新对象:

        delete o.b;
        o // { a: 1 }
        p // { a: 1 }
        

        让我们通过p更新对象:

        p.b = 2;
        o // { a: 1, b: 2 }
        p // { a: 1, b: 2 }
        

        如您所见,op 是同步的。


        让我们“重新初始化”o

        o = { a: o.a };
        

        op 现在指的是不同的对象:

        o // { a: 1 }
        p // { a: 1, b: 2 }
        o === p // false
        

        让我们更新存储在o 中的对象:

        o.c = 3;
        o // { a: 1, c: 3 }
        p // { a: 1, b: 2 }
        

        让我们更新存储在p 中的对象:

        delete p.a;
        o // { a: 1, c: 3 }
        p // { b: 2 }
        

        如您所见,op 不再同步。


        问题是:你想保持两个变量(op)同步吗?如果是,则VisioN's answer的第二个代码块是正确的,否则,选择第一个代码块。

        【讨论】:

          【解决方案8】:

          这是谷歌搜索“js 只保留某些键”时的第一次点击,因此可能值得更新。

          最“优雅”的解决方案可能只是使用 underscore.js

          _.pick(myObj, 'p1', 'p2', 'p100')
          

          【讨论】:

          • ...或lodash
          • @x-yuri 你能详细说明一下吗? lodash.pick 之类的东西,或者你在暗示什么?还是来自 lodash 本身的 _.pick
          • @MattSom 我的意思是lodash 现在应该更受欢迎。关于lodash.pick,不,你称呼它的方式在lodashunderscore.js中是相似的。但它可能是lodash.pick,这取决于您如何使其可用于脚本。
          【解决方案9】:

          Lodash 有一个名为 pick 的函数,它可以满足您的描述。我知道包含一个库并不理想,但你也可以在使用包时挑选函数等。

          var myObj={
              p1:123,
              p2:321,
              p3:{p3_1:1231,p3_2:342},
              p4:'23423',
              //....
              p99:{p99_1:'sadf',p99_2:234},
              p100:3434
          }
          
          var newObj = _.pick(myObj, 'p1', 'p2', 'p100')
          

          【讨论】:

            【解决方案10】:

            您可以编写自己的_.pick 实现,并根据需要使用它。

            以这段代码的 sn-p 作为以下情况的基础:

            const myObj={
                p1:123,
                p2:321,
                p3:{p3_1:1231,p3_2:342},
                p4:'23423',
                //....
                p99:{p99_1:'sadf',p99_2:234},
                p100:3434
            }
            let properties= ['p1','p2', 'p3', 'p100'];
            

            案例一:

            你想要一个浅拷贝(引用向量值)

            const myNewObj = properties.reduce((newObj,property)=>{newObj[property] = myObj[property]; return newObj}, {})
            // if we modify the original vector value of 'p3' in `myObj` we will modify the copy as well:
            
            myObj.p3.p3_1 = 99999999999;
            
            console.log(myNewObj); // { p1: 123,​​​​​​​​​​  p2: 321,​​​​​​​​​​  p3: { p3_1: 99999999999, p3_2: 42 },​​​​​​​​​​  p100: 3434 }​​​​​
            

            案例2:

            您想要一个深拷贝(丢失对向量值的引用)

            您可以使用JSON 实用程序来解决这个问题。

            const myNewObj2 = properties.reduce((newObj,property)=>{newObj[property] = JSON.parse(JSON.stringify(myObj[property])); return newObj},{})
            
            // no matter how hard you modify the original object, you will create a new independent object
            myObj.p3.p3_1 = 99999999999;
            
            console.log(myNewObj2) // { p1: 123, p2: 321, p3: { p3_1: 1231, p3_2: 342 }, p100: 3434 }​​​​​
            

            用函数重用案例 2

            你可以实现一个 reducer 来在不同的场景中使用它,比如这个:

            function reduceSelectedProps(origin, properties){
              return (newObj,property)=>{
                newObj[property] = JSON.parse(JSON.stringify(origin[property]));
                 return newObj
              }
            }
            

            所以你可以更优雅地重用它:

            const myNewObj3 = properties.reduce(reduceSelectedProps(myObj, properties),{});
            // no matter how hard you modify the original object, you will create a new independent object
            myObj.p3.p3_1 = 99999999999;
            
            console.log(myNewObj3) // { p1: 123, p2: 321, p3: { p3_1: 1231, p3_2: 342 }, p100: 3434 }​​​​​
            

            免责声明:

            这只是一个不处理属性内的DateSetMapfunction 值的示例。为了处理所有这些情况(以及许多其他情况),它需要一个非常复杂的函数来检查原型和所有这些东西。此时,请考虑使用任何可以做到这一点的库来重用其他开发人员的工作。罗达什?

            【讨论】:

              【解决方案11】:

              我为我有一个包含对象的数组的情况制作了这个简短的解决方案。

              那么考虑下面的数组吗?

              arr=[{"a1":"A1","b1":"B1"},{"a1":"Z1","b1":"X1"}];
              console.log(arr);

              我只想保留所有对象的“b1”属性。

              您可以使用 map() 并按如下方式删除:

              arr=[{"a1":"A1","b1":"B1"},{"a1":"Z1","b1":"X1"}];
              arr=arr.map(function(d){
                      delete d["a1"];
                      return d;
              
                  });
              console.log(arr);

              result 是一个包含对象但只有“b1”属性的数组。

              【讨论】:

                【解决方案12】:

                您可以使用这种方法:

                let result = (({ p1, p2, p100 }) =&gt; ({ p1, p2, p100 }))(myObj);

                我在https://stackoverflow.com/a/25554551/470749学到的。

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2014-08-17
                  • 1970-01-01
                  • 2019-06-22
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多