ES6中按键过滤对象属性

497

假设我有一个对象:

{
  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的扩展运算符来实现一个简洁的方法。


ES6没有对象展开运算符,而且在这里你也不需要它们。 - Bergi
1
可能是JavaScript:filter()用于对象的重复问题。 - Jonathan H
@DanDascalescu 但是这个答案提供了一种ES6的方法来完成OP所要求的,不是吗? - Jonathan H
2
如果我想按键/值进行过滤怎么办? - jmchauv
31个回答

10
以下解决方案适用于大型数据集,因为它只需要遍历一次白名单,而不是每个对象属性都需要遍历白名单,从而更加高效:
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);


9

最近我这样做了:

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

2
嗯,看起来你混合了旧的和新的语法。Object.assign == ... 你可以写成 const dummyObj = { ...obj }const target = { ...dummyObj }。而且,后者并不是必要的,因为你之后可以直接使用 dummyObj 进行操作。 - Andy
1
这段代码会对一个对象进行三次复制。只为了得到其中一个已删除键的副本:dummyObj = Object.assign({}, obj) - 将 obj 克隆到一个名为 dummyObj 的新对象中;delete dummyObj[key]; - 删除该键。此时,你的工作完成了。但是:{...dummyObj} - 将 dummyObj(已经是克隆)克隆到一个临时对象中;target = Object.assign({}, {...dummyObj}) - 将克隆的临时对象(克隆的克隆)克隆到一个名为 target 的新对象中。 - VLAZ

8

使用 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
},{})

6

简单易懂的方式!完成此操作。

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})


你在这里实际上并没有过滤任何东西,只是解构给定的数据。但是当数据发生变化时会发生什么? - Idris Dopico Peña
@IdrisDopicoPeña - 解构就是过滤器。使用字段名称的对象与使用字段名称的数组有何不同?(尽管此解决方案将添加缺失的键) - charles-allen
1
@charles-allen 一个字段名称的数组可以更容易地进行更改,而不需要更改使用它的代码。因此,您可以拥有一个可重用的函数,该函数接受该数组并过滤您的对象,例如,myFunc(["item1","item3"])稍后可以重复使用为myFunc(["foo","bar"]),而无需更改其工作方式。使用这种方法,您需要为要筛选键的每个对象编写不同的函数。 - VLAZ
@VLAZ - 确实。也许我误解了Idris的意思。我以为他们指的是不同的“myData”,而不是不同的筛选器(这是我的用例 - 我知道我在编译时需要哪些属性,所以这个解决方案更加简洁)。 - charles-allen
@charles-allen 我不是说这个解决方案是错的。它确实非常有用。然而,问题是什么区别。如果我需要一次提取“item1”和“item3”,然后另一次需要“foo”和“bar”,我可能会选择两次解构。如果我需要经常这样做,或者我甚至不知道我想要哪些,那么我会使用一个函数。 - VLAZ
@VLAZ - 我完全同意你的观点! - charles-allen

6
借鉴 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作为参数传入。

5
你可以像这样做:

你可以这样操作:

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)。


这太糟糕了。我知道 with 有其(非常少的)用途,但这不是其中之一。这只是多了一些步骤的解构。source => { with(source){ return {item1, item3} } } 等同于 ({item1, item3}) => ({item1, item3}) 这已经是一个答案了 - VLAZ

5

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

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

该函数将返回另一个仅包含白名单属性的对象。请注意,keyvalue也包含在列表中。


喜欢这个!可能非常快速执行,而且很容易过滤掉一个或多个属性。 - Janis Jansen
1
@JanisJansen 对象的序列化和反序列化对我来说似乎不是很快。此外,您将失去像函数、undefinedNaN或BigInts这样的值。 - VLAZ

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

3
还有其他回答提供了原帖的问题,并且它们在一段时间前发布。发表答案时,请确保添加一个新解决方案或更加充分的解释,尤其是在回答较旧的问题时。 - help-info.de

3

有许多方法可以实现这一点。 被接受的答案 使用了一个键 - 过滤器 - 缩减的方法,这不是性能最好的方法。

相反地,使用 for...in 循环来遍历对象的键,或者循环遍历允许的键,然后再创建一个新对象,这种方法的性能提升约50%a

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

a. 请参阅jsPerf,了解一个简单用例的基准测试结果。由于浏览器的不同,结果可能会有所不同。


2
以下方法接受对象和要过滤的任何属性。

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);


这会导致一个 linting 错误,因为 _ 没有在其他地方使用,但它非常有创意。 - anakha

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接