繁体   English   中英

在 ES6 中按键过滤对象属性

[英]Filter object properties by key in ES6

假设我有一个对象:

{
  item1: { key: 'sdfd', value:'sdfd' },
  item2: { key: 'sdfd', value:'sdfd' },
  item3: { key: 'sdfd', value:'sdfd' }
}

我想通过过滤上面的对象来创建另一个对象,所以我有类似的东西。

 {
    item1: { key: 'sdfd', value:'sdfd' },
    item3: { key: 'sdfd', value:'sdfd' }
 }

我正在寻找一种使用 Es6 完成此操作的干净方法,因此我可以使用扩展运算符。

如果您有一个允许值列表,您可以使用以下方法轻松地将它们保留在对象中:

 const raw = { item1: { key: 'sdfd', value:'sdfd' }, item2: { key: 'sdfd', value:'sdfd' }, item3: { key: 'sdfd', value:'sdfd' } }; const allowed = ['item1', 'item3']; const filtered = Object.keys(raw) .filter(key => allowed.includes(key)) .reduce((obj, key) => { obj[key] = raw[key]; return obj; }, {}); console.log(filtered);

这使用:

  1. Object.keys列出的所有属性raw (原始数据),然后
  2. Array.prototype.filter选择存在于允许列表中的键,使用
    1. Array.prototype.includes以确保它们存在
  3. Array.prototype.reduce以仅使用允许的属性构建新对象。

这将使用允许的属性进行浅拷贝(但不会复制属性本身)。

您还可以使用对象扩展运算符来创建一系列对象而不改变它们(感谢rjerue 提到这一点):

 const raw = { item1: { key: 'sdfd', value:'sdfd' }, item2: { key: 'sdfd', value:'sdfd' }, item3: { key: 'sdfd', value:'sdfd' } }; const allowed = ['item1', 'item3']; const filtered = Object.keys(raw) .filter(key => allowed.includes(key)) .reduce((obj, key) => { return { ...obj, [key]: raw[key] }; }, {}); console.log(filtered);

出于琐事的目的,如果您想从原始数据中删除不需要的字段(我建议这样做,因为它涉及一些丑陋的突变),您可以像这样反转includes检查:

 const raw = { item1: { key: 'sdfd', value:'sdfd' }, item2: { key: 'sdfd', value:'sdfd' }, item3: { key: 'sdfd', value:'sdfd' } }; const allowed = ['item1', 'item3']; Object.keys(raw) .filter(key => !allowed.includes(key)) .forEach(key => delete raw[key]); console.log(raw);

我包含这个例子是为了展示一个基于突变的解决方案,但我不建议使用它。

如果你都OK使用ES6语法,我发现,最干净的方式来做到这一点,因为注意到这里这里是:

const data = {
  item1: { key: 'sdfd', value:'sdfd' },
  item2: { key: 'sdfd', value:'sdfd' },
  item3: { key: 'sdfd', value:'sdfd' }
};

const { item2, ...newData } = data;

现在, newData包含:

{
  item1: { key: 'sdfd', value:'sdfd' },
  item3: { key: 'sdfd', value:'sdfd' }
};

或者,如果您将密钥存储为字符串:

const key = 'item2';
const { [key]: _, ...newData } = data;

在后一种情况下, [key]被转换为item2但由于您使用的是const分配,因此您需要为分配指定一个名称。 _表示丢弃值。

更普遍:

const { item2, ...newData } = data; // Assign item2 to item2
const { item2: someVarName, ...newData } = data; // Assign item2 to someVarName
const { item2: _, ...newData } = data; // Assign item2 to _
const { ['item2']: _, ...newData } = data; // Convert string to key first, ...

这不仅将您的操作简化为单行操作,而且还不需要您知道其他键是什么(您想要保留的键)。

一个简单的效用函数如下所示:

function removePropFromObject(obj, prop) {
  const { [prop]: _, ...rest } = obj
  return { ...rest }
}

你能找到的最干净的方法是使用Lodash#pick

const _ = require('lodash');

const allowed = ['item1', 'item3'];

const obj = {
  item1: { key: 'sdfd', value:'sdfd' },
  item2: { key: 'sdfd', value:'sdfd' },
  item3: { key: 'sdfd', value:'sdfd' }
}

const filteredObj = _.pick(obj, allowed)

没有什么之前没有说过,但结合一般 ES6 答案的一些答案:

 const raw = { item1: { key: 'sdfd', value: 'sdfd' }, item2: { key: 'sdfd', value: 'sdfd' }, item3: { key: 'sdfd', value: 'sdfd' } }; const filteredKeys = ['item1', 'item3']; const filtered = filteredKeys .reduce((obj, key) => ({ ...obj, [key]: raw[key] }), {}); console.log(filtered);

您现在可以使用Object.fromEntries方法使其更短更简单(检查浏览器支持):

const raw = { item1: { prop:'1' }, item2: { prop:'2' }, item3: { prop:'3' } };

const allowed = ['item1', 'item3'];

const filtered = Object.fromEntries(
   Object.entries(raw).filter(
      ([key, val])=>allowed.includes(key)
   )
);

阅读更多关于: Object.fromEntries

只是现代 JS 一行中的另一种解决方案,没有外部库

我正在玩“解构”功能:

 const raw = { item1: { key: 'sdfd', value: 'sdfd' }, item2: { key: 'sdfd', value: 'sdfd' }, item3: { key: 'sdfd', value: 'sdfd' } }; var myNewRaw = (({ item1, item3}) => ({ item1, item3 }))(raw); console.log(myNewRaw);

好的,这个单线怎么样

    const raw = {
      item1: { key: 'sdfd', value: 'sdfd' },
      item2: { key: 'sdfd', value: 'sdfd' },
      item3: { key: 'sdfd', value: 'sdfd' }
    };

    const filteredKeys = ['item1', 'item3'];

    const filtered = Object.assign({}, ...filteredKeys.map(key=> ({[key]:raw[key]})));

你可以添加一个通用的ofilter (用通用的oreduce实现),这样你就可以像处理数组一样轻松地过滤对象——

const oreduce = (f, acc, o) =>
  Object
    .entries (o)
    .reduce
      ( (acc, [ k, v ]) => f (acc, v, k, o)
      , acc
      )

const ofilter = (f, o) =>
  oreduce
    ( (acc, v, k, o)=>
        f (v, k, o)
          ? Object.assign (acc, {[k]: v})
          : acc
    , {}
    , o
    )

我们可以看到它在这里工作 -

const data =
  { item1: { key: 'a', value: 1 }
  , item2: { key: 'b', value: 2 }
  , item3: { key: 'c', value: 3 }
  }

console.log
  ( ofilter
      ( (v, k) => k !== 'item2'
      , data
      )
      // [ { item1: { key: 'a', value: 1 } }
      // , { item3: { key: 'c', value: 3 } }
      // ]

  , ofilter
      ( x => x.value === 3
      , data
      )
      // [ { item3: { key: 'c', value: 3 } } ]
  )

在以下您自己的浏览器中验证结果 -

 const oreduce = (f, acc, o) => Object .entries (o) .reduce ( (acc, [ k, v ]) => f (acc, v, k, o) , acc ) const ofilter = (f, o) => oreduce ( (acc, v, k, o)=> f (v, k, o) ? Object.assign (acc, { [k]: v }) : acc , {} , o ) const data = { item1: { key: 'a', value: 1 } , item2: { key: 'b', value: 2 } , item3: { key: 'c', value: 3 } } console.log ( ofilter ( (v, k) => k !== 'item2' , data ) // [ { item1: { key: 'a', value: 1 } } // , { item3: { key: 'c', value: 3 } } // ] , ofilter ( x => x.value === 3 , data ) // [ { item3: { key: 'c', value: 3 } } ] )

这两个功能可以通过多种方式实现。 我选择在oreduce附加到Array.prototype.reduce但你也可以很容易地从头开始写

在允许的键上使用Array.reduce方法的另一种解决方案:

const raw = {
  item1: { key: 'sdfd', value:'sdfd' },
  item2: { key: 'sdfd', value:'sdfd' },
  item3: { key: 'sdfd', value:'sdfd' }
};

const allowed = ['item1', 'item3'];

const filtered = allowed.reduce((obj, key) => { 
  obj[key] = raw[key]; 
  return obj 
}, {})

console.log(filtered);

特别是在较大的源对象(在本例中为原始对象)的情况下,这将是有意义的。 不会使用源的所有条目执行迭代,而只会使用要过滤的键,因此更短/更快......

这个小提琴中演示......


但我不得不说我也喜欢这个答案中的解决方案,它使用Object.fromEntries Array.filterArray.includes

 const object = Object.fromEntries( Object.entries(raw).filter(([key, value]) => allowed.includes(key)) );

这个小提琴中演示......

我最近是这样做的:

const dummyObj = Object.assign({}, obj);
delete dummyObj[key];
const target = Object.assign({}, {...dummyObj});

这里的答案绝对合适,但它们有点慢,因为它们需要遍历对象中每个属性的白名单。 对于大型数据集,下面的解决方案要快得多,因为它只循环访问白名单一次:

 const data = { allowed1: 'blah', allowed2: 'blah blah', notAllowed: 'woah', superSensitiveInfo: 'whooooah', allowed3: 'bleh' }; const whitelist = ['allowed1', 'allowed2', 'allowed3']; function sanitize(data, whitelist) { return whitelist.reduce( (result, key) => data[key] !== undefined ? Object.assign(result, { [key]: data[key] }) : result, {} ); } const result = sanitize(data, whitelist); console.log(result);

使用Object.entries()而不是Object.keys()可以实现不使用filter更简单的解决方案

const raw = {
  item1: { key: 'sdfd', value:'sdfd' },
  item2: { key: 'sdfd', value:'sdfd' },
  item3: { key: 'sdfd', value:'sdfd' }
};

const allowed = ['item1', 'item3'];

const filtered = Object.entries(raw).reduce((acc,elm)=>{
  const [k,v] = elm
  if (allowed.includes(k)) {
    acc[k] = v 
  }
  return acc
},{})

捎带ssube 的回答

这是一个可重复使用的版本。

Object.filterByKey = function (obj, predicate) {
  return Object.keys(obj)
    .filter(key => predicate(key))
    .reduce((out, key) => {
      out[key] = obj[key];
      return out;
    }, {});
}

调用它使用

const raw = {
  item1: { key: 'sdfd', value:'sdfd' },
  item2: { key: 'sdfd', value:'sdfd' },
  item3: { key: 'sdfd', value:'sdfd' }
};

const allowed = ['item1', 'item3'];

var filtered = Object.filterByKey(raw, key => 
  return allowed.includes(key));
});

console.log(filtered);

ES6 箭头函数的美妙之处在于您不必将allowed作为参数传入。

简单的方法! 去做这个。

 const myData = { item1: { key: 'sdfd', value:'sdfd' }, item2: { key: 'sdfd', value:'sdfd' }, item3: { key: 'sdfd', value:'sdfd' } }; const{item1,item3}=myData const result =({item1,item3})

你可以这样做:

const base = {
  item1: { key: 'sdfd', value:'sdfd' },
  item2: { key: 'sdfd', value:'sdfd' },
  item3: { key: 'sdfd', value:'sdfd' }
};

const filtered = (
    source => { 
        with(source){ 
            return {item1, item3} 
        } 
    }
)(base);

// one line
const filtered = (source => { with(source){ return {item1, item3} } })(base);

这有效但不是很清楚,另外不推荐with语句( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/with )。

我知道这已经有很多答案了,这是一个相当古老的问题。 但我只是想出了这个简洁的单行:

JSON.parse(JSON.stringify(raw, ['key', 'value', 'item1', 'item3']))

这将返回另一个仅具有白名单属性的对象。 请注意, keyvalue包含在列表中。

有很多方法可以实现这一点。 接受的答案使用 Keys-Filter-Reduce 方法,这不是最高效的。

取而代之的是,使用for...in通过的物体的键循环来循环,或通过允许键循环,然后构成一个新的对象是〜50%更好的性能一个

const obj = {
  item1: { key: 'sdfd', value:'sdfd' },
  item2: { key: 'sdfd', value:'sdfd' },
  item3: { key: 'sdfd', value:'sdfd' }
};

const keys = ['item1', 'item3'];

function keysReduce (obj, keys) {
  return keys.reduce((acc, key) => {
    if(obj[key] !== undefined) {
      acc[key] = obj[key];
    }
    return acc;
  }, {});
};

function forInCompose (obj, keys) {
  const returnObj = {};
  for (const key in obj) {
    if(keys.includes(key)) {
      returnObj[key] = obj[key]
    }
  };
  return returnObj;
};

keysReduce(obj, keys);   // Faster if the list of allowed keys are short
forInCompose(obj, keys); // Faster if the number of object properties are low

一种。 有关简单用例的基准测试,请参阅jsPerf 结果会因浏览器而异。

const filteredObject = Object.fromEntries(Object.entries(originalObject).filter(([key, value]) => key !== uuid))

您可以删除对象上的特定属性

items={
  item1: { key: 'sdfd', value:'sdfd' },
  item2: { key: 'sdfd', value:'sdfd' },
  item3: { key: 'sdfd', value:'sdfd' }
}

// Example 1
var key = "item2";
delete items[key]; 

// Example 2
delete items["item2"];

// Example 3
delete items.item2;

我很惊讶怎么还没有人提出这个建议。 它非常干净并且非常明确地说明了您要保留哪些键。

const unfilteredObj = {a: ..., b:..., c:..., x:..., y:...}

const filterObject = ({a,b,c}) => ({a,b,c})
const filteredObject = filterObject(unfilteredObject)

或者,如果您想要一个脏的单衬:

const unfilteredObj = {a: ..., b:..., c:..., x:..., y:...}

const filteredObject = (({a,b,c})=>({a,b,c}))(unfilteredObject);

另一个捷径

 function filterByKey(v,keys){ const newObj ={}; keys.forEach(key=>{v[key]?newObj[key]=v[key]:''}); return newObj; } //given let obj ={ foo: "bar", baz: 42,baz2:"blabla" , "spider":"man", monkey:true}; //when let outObj =filterByKey(obj,["bar","baz2","monkey"]); //then console.log(outObj); //{ // "baz2": "blabla", // "monkey": true //}

上述许多解决方案对raw每个键重复调用Array.prototype.includes ,这将使解决方案 O(n·m) (其中 n 是对象中的键数,m 是允许列表的长度) .

这可以通过使用允许的 Set 来避免,但迭代allowed键并将它们复制到初始为空的对象会给出非常简单、可读的代码,即 O(m):

 const raw = { item1: { key: 'sdfd', value:'sdfd' }, item2: { key: 'sdfd', value:'sdfd' }, item3: { key: 'sdfd', value:'sdfd' } }; const allowed = ['item1', 'item3']; const filtered = {}; for (const key of allowed) { if (key in raw) filtered[key] = raw[key]; } console.log(filtered);

如果您想避免复制继承的属性,您也可以使用raw.hasOwnProperty(key)而不是key in raw

好的,这个怎么样:

const myData = {
  item1: { key: 'sdfd', value:'sdfd' },
  item2: { key: 'sdfd', value:'sdfd' },
  item3: { key: 'sdfd', value:'sdfd' }
};

function filteredObject(obj, filter) {
  if(!Array.isArray(filter)) {
   filter = [filter.toString()];
  }
  const newObj = {};
  for(i in obj) {
    if(!filter.includes(i)) {
      newObj[i] = obj[i];
    }
  }
  return newObj;
}

并这样称呼它:

filteredObject(myData, ['item2']); //{item1: { key: 'sdfd', value:'sdfd' }, item3: { key: 'sdfd', value:'sdfd' }}

此函数将根据键列表过滤对象,它比之前的答案更有效,因为它在调用 reduce 之前不必使用 Array.filter。 所以它的 O(n) 而不是 O(n + 过滤)

function filterObjectByKeys (object, keys) {
  return Object.keys(object).reduce((accum, key) => {
    if (keys.includes(key)) {
      return { ...accum, [key]: object[key] }
    } else {
      return accum
    }
  }, {})
}

以下方法采用要过滤掉的对象和任何属性。

 function removeObjectKeys(obj, ...keysToRemove) { let mObject = { ...obj } for (let key of keysToRemove) { const { [String(key)]: _, ...rest } = mObject mObject = { ...rest } } return mObject } const obj = { 123: "hello", 345: "world", 567: "and kitty" }; const filtered = removeObjectKeys(obj, 123); console.log(filtered); const twoFiltered = removeObjectKeys(obj, 345, 567); console.log(twoFiltered);

基于这两个答案:

https://stackoverflow.com/a/56081419/13819049
https://stackoverflow.com/a/54976713/13819049

我们可以做的:

const original = { a: 1, b: 2, c: 3 };
const allowed = ['a', 'b'];

const filtered = Object.fromEntries(allowed.map(k => [k, original[k]]));

哪个更干净更快:

https://jsbench.me/swkv2cbgkd/1

在循环期间,当遇到某些属性/键时不返回任何内容并继续其余的:

const loop = product =>
Object.keys(product).map(key => {
    if (key === "_id" || key === "__v") {
        return; 
    }
    return (
        <ul className="list-group">
            <li>
                {product[key]}
                <span>
                    {key}
                </span>
            </li>
        </ul>
    );
});

另一种方法是使用Array.prototype.forEach()作为

 const raw = { item1: { key: 'sdfd', value: 'sdfd' }, item2: { key: 'sdfd', value: 'sdfd' }, item3: { key: 'sdfd', value: 'sdfd' } }; const allowed = ['item1', 'item3', 'lll']; var finalObj = {}; allowed.forEach(allowedVal => { if (raw[allowedVal]) finalObj[allowedVal] = raw[allowedVal] }) console.log(finalObj)

它仅包含原始数据中可用的那些键的值,从而防止添加任何垃圾数据。

那将是我的解决方案:

const filterObject = (obj, condition) => {
    const filteredObj = {};
    Object.keys(obj).map(key => {
      if (condition(key)) {
        dataFiltered[key] = obj[key];
      }
    });
  return filteredObj;
}

使用PropPick


pick('item1 item3', obj);
// {
//   item1: { key: 'sdfd', value:'sdfd' },
//   item3: { key: 'sdfd', value:'sdfd' }
// }

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM