ES6のキーでオブジェクトプロパティをフィルタリングする


266

私がオブジェクトを持っているとしましょう:

{
  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


@DanDascalescuしかし、この答えは、OP6 が要求することを達成するES6の方法を提供しますね。
ジョナサンH

キー/値でフィルタリングしたい場合はどうなりますか?
jmchauv

回答:


503

許可された値のリストがある場合は、次を使用してオブジェクトに簡単に保持できます。

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.keysraw(元のデータ)のすべてのプロパティをリストするには、
  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);

突然変異ベースのソリューションを示すためにこの例を含めていますが、それを使用することはお勧めしません。


1
ありがとうございました。また、「分解構文」を使用したアプローチも見つけました。IE:const {item1、item3} = raw const newObject = {item1、item3}
29er

2
分解は機能します(問題なく動作します)が、純粋にコンパイル時です。プロパティの動的リストにしたり、複雑なルールを提供したりすることはできません(たとえば、ループに検証コールバックをアタッチすることができます)。
ssube

3
これには十分投票できません!filterを使用して、forループから別のオブジェクトを作成および削減しないことは、よくできました。そして、不変バージョンと可変バージョンを明示的に分離したのは素晴らしいことです。1
スキマスイッチ

3
クイック警告:Array.prototype.includesES6の一部ではありません。ECMAScript 2016(ES7)で導入されました。
Vineet 2017年

3
不変の方法で削減を行う場合は、関数の内容をreturn {... obj、[key]:raw [key]}で置き換えることもできます
rjerue

93

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, ...

これにより、操作が1行に削減されるだけでなく、他のキー(保持したいキー)を知る必要もありません。


5
_使い捨ての価値を表す」これはどこに来るのでしょうか?初めて見たとき
yhabib 2017

5
これはJSコミュニティが採択した慣習だと思います。アンは_単にJSで使用することができるが、それはかなり無名ですので、あなたが意図伝えることを気にしている場合、それは本当にそのように使用すべきではない有効な変数名です。そのため、気にしない変数を表す方法として採用されています。これについての詳細は次のとおりです。stackoverflow.com
Ryan H.

2
これは、受け入れ答えよりもはるかに整然とあるObject.keys(新しい配列を作成するオーバーヘッド)、およびで配列を繰り返し処理のオーバーヘッドを回避filterし、をreduce
ericsoco

3
@yhabibは関係あり_ません。それは単なる変数名です。好きな名前に変更できます
Vic

1
@Gerrat一般化された解決策は簡単なワンライナーになるとは思いません。そのために、lodashのomit関数を使用します:lodash.com/docs/4.17.10#omitまたはここに示されている他のソリューションの1つ。
Ryan H.

42

あなたが見つけることができる最もきれいな方法は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)

3
そのためには、実行時に追加のプロジェクト依存関係をダウンロードする必要があることを指摘することが重要です。
S.エステベス

1
_.omit(obj、["item2"])-lodash.omitはlodash.pickの反対です
Alexander Solovyev

29

これまで言われていないことは何もありませんが、いくつかの回答を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);


1
これは他よりもはるかにシンプルでパフォーマンスの高いソリューションです;)
pomeh

26

外部ライブラリのないModern JSの1行の別のソリューション。

私は「破壊」機能で遊んでいました:

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


2
var myNewRaw = (({ item1, item3}) => ({ item1, item3 }))(raw);構文の問題
Awol 2018年

1
ここにはいくつかのことが要約されています。まず最初に、関数の宣言があります。これはアロー関数です(オブジェクトを受け取り、オブジェクトを返します)。function(obj){return obj;}と同じです。2つ目は、ES6が将来の破壊を可能にすることです。私の関数宣言では、オブジェクト{item1、item3}を分解しています。そして最後に、自分で関数を呼び出します。たとえば、自己呼び出し機能を使用してスコープを管理できます。しかし、ここではコードを圧縮するだけでした。私はそれが明確であることを望みます。何かを見逃した場合は、自由に追加してください。
Novy

5
これは、推奨される最新のアプローチimho
mattdlockyer

20

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


1
これは今、私が考える最良の方法です。削減よりもはるかに直感的です。
デイモン

10

ジェネリック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 } } ]
  )

以下のブラウザで結果を確認してください–

これら2つの機能は、さまざまな方法で実装できます。私はArray.prototype.reduce内側に取り付けることを選びましたoreduceが、それをすべてゼロから簡単に書くことができました


あなたのソリューションのように私が、私はそれが比較される方法をより明確に/より効率的かわからない、この1
Jonathan H

3
以下は、ソリューションが最速であることを示すベンチマークです。
Jonathan H

oreduce第一のaccに隠されている.reduce(acc, k)、とに隠されているが- という変数と呼ばれている-も?ofilterooreduceo
Jarrod Mosen 2017

ofilter変数内oはシャドウされていますが、常に同じ入力変数を指しています。これがシャドウされている理由です。同じなので、アキュムレータ(acc)もシャドウされます。これは、ラムダを介してデータがどのように移動するかをより明確に示すためです。accは常に同じバインディングであるとは限りませんが、常に返したい計算結果の現在の永続的な状態を表し
ます

コードはうまく機能し、メソッドは非常に優れていますが、JavaScriptのヘルプを明らかに必要としている人にとって、そのようなコードを書くのに役立つものは何だろうと思います。私は簡潔にするためにすべてですが、それは最小化されたコードとほとんど同じくらいコンパクトです。
ポールGミハイ

7

これは私が最近やった方法です:

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

うーん。古い構文と新しい構文が混在しているようです。Object.assign == ...あなたは書くことができますconst dummyObj = { ...obj }const target = { ...dummyObj }。さらに、後でdummyObjを直接操作できるため、後者はまったく必要ありません。
アンディ

7

わかりました、このワンライナーはどうですか

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

2
すべての答えの最も驚くべき解決策。1
GergőHorvathの

削除したいキーだけがわかっている場合はどうすればよいですか。
Jason

6

ここでの回答は間違いなく適切ですが、オブジェクトのすべてのプロパティのホワイトリストをループする必要があるため、少し遅くなります。以下のソリューションは、ホワイトリストを1回だけループするため、大規模なデータセットの場合ははるかに高速です。

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

  sanitize(data, whitelist)

5

上のピギーバックssubeのansweranswer

これは再利用可能なバージョンです。

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、パラメーターとして渡す必要がないことです。


4

あなたはこのようなことをすることができます:

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


4

使用せずに簡単な解決策をfilter用いて達成することができるObject.entries()代わりに、Object.keys()

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

3

これを達成するには多くの方法があります。受け入れ答え最もパフォーマンスではありませんキーズ-FILTER-削減のアプローチを、使用しています。

代わりに、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

a。簡単な使用例のベンチマークについては、jsPerfを参照してください。結果はブラウザによって異なります。


3

オブジェクトの特定のキーを削除できます

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;

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

2
OPの質問を提供する他の回答があり、それらはしばらく前に投稿されました。回答を投稿するときは、特に古い質問に回答する場合は、新しい解決策または大幅に優れた説明を追加してください。
help-info.de

2

シンプルな方法!これをする。

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


ここでは実際には何もフィルタリングせず、指定されたデータを破壊します。しかし、データが変更されるとどうなりますか?
イドリスドピコペーニャ

2

「新しい」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)) );

このフィドルでのデモ...


ウィルト、あなたの2番目のアプローチは、昨年提供されたこの回答の完全な複製です:stackoverflow.com/a/56081419/5522000
Art Schmidt

@ArtSchmidtコメントありがとうございます:長いリストでその1つを逃しました。代わりにその答えを参照します。
ウィルト

1

OK、これどうですか:

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

1

この関数は、キーのリストに基づいてオブジェクトをフィルタリングします。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
    }
  }, {})
}

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

1

まだ誰もこれを提案していないことに驚いています。それは非常にクリーンで、どのキーを保持したいかについて非常に明確です。

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

このメソッドは、元の配列に存在しないキーを追加します。問題かもしれませんが、これは少なくとも指摘されるべきです。
ポンキエット

0

別のアプローチは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)

これには、生データで使用可能なキーのみの値が含まれているため、ジャンクデータの追加が防止されます。


0

それが私の解決策です:

const filterObject = (obj, condition) => {
    const filteredObj = {};
    Object.keys(obj).map(key => {
      if (condition(key)) {
        dataFiltered[key] = obj[key];
      }
    });
  return filteredObj;
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.