質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

jQuery

jQueryは、JavaScriptライブラリのひとつです。 簡単な記述で、JavaScriptコードを実行できるように設計されています。 2006年1月に、ジョン・レシグが発表しました。 jQueryは独特の記述法を用いており、機能のほとんどは「$関数」や「jQueryオブジェクト」のメソッドとして定義されています。

Q&A

4回答

400閲覧

JSのオブジェクトについて、指定値を持つキーを配列にしたい

kaminarikun

総合スコア8

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

jQuery

jQueryは、JavaScriptライブラリのひとつです。 簡単な記述で、JavaScriptコードを実行できるように設計されています。 2006年1月に、ジョン・レシグが発表しました。 jQueryは独特の記述法を用いており、機能のほとんどは「$関数」や「jQueryオブジェクト」のメソッドとして定義されています。

0グッド

1クリップ

投稿2020/01/15 23:34

以下のオブジェクトtwoについて、trueが1つ以上ある場合だけ、その親のキーを配列として取得したいです。

つまりsmithjohntrueが1つ以上あるので、resultとして欲しいのは['smith','john']となります。

js

1// オブジェクト 2var obj = { 3 one:{ 4 two:{ 5 smith:{ a:true, b:false }, 6 john :{ a:true, b:true, c:true }, 7 }, 8 }, 9} 10 11// trueが1つ以上のものを取得 12let trues = []; 13const two = obj.one.two; 14Object.keys(two).forEach(function(name){ 15 const menu = two[name]; 16 Object.values(menu).forEach(function(state){ 17 if(state===true){ 18 trues.push(name) 19 } 20 }); 21}); 22 23// 重複を削除 24var result = trues.filter(function(x,i,self){ 25 return self.indexOf(x) === i; 26}); 27 28// 結果 29console.log(result);

上記でできてはいるのですが、配列truesが重複してしまうために、filterをかけなければならなくなってしまいました。

もう少しうまい方法があればと思っているのですが、よろしければ効率的な方法についてご意見をいただけませんでしょうか。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

miyabi-sun

2020/01/16 02:31

> 配列truesが重複してしまうために、filterをかけなければならなくなってしまいました。 オブジェクトは重複したキーの値を所持出来ないのでありえません。 他に何かをしたかったのだと思いますが具体的にはなんでしょうか?
thyda.eiqau

2020/01/16 04:56

miyabi-sunさん、「配列truesが重複してしまう」というのは単純に、現状のコードで「// 重複を削除」のコメント行時点では ["smith", "john", "john", "john"] になってしまうことを意味しているのではないかと思います。
guest

回答4

0

Object.keys(two).filter(key => Object.values(two[key]).includes(true));

投稿2020/01/16 00:00

thyda.eiqau

総合スコア2982

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

Lodashチェイン記法を使えば一撃です。

上記のQiitaの記事では_(val)スタートの場合はプリミティブ値かLodashオブジェクトか戻り値か分からないみたいな事が書いてありますが、
このメソッドはどちらを返すかはドキュメントに明記されています。
面倒ならば_.chain(val)始動で統一すれば良いです。

js

1const obj = { 2 one:{ 3 two:{ 4 smith:{ a:true, b:false }, 5 john :{ a:true, b:true, c:true }, 6 }, 7 }, 8}; 9const trues = _.chain(obj.one.two) 10 .toPairs() 11 .filter(([key, val]) => Object.values(val).includes(true)) 12 .map(([key]) => key) 13 .value(); 14 15result = trues;

Online Lodash Testerはresult変数に値を突っ込んだら結果を返してくれる仕組みなので結果をresult変数に突っ込んで…
はい

[ "smith", "john" ]

ユニークなものだけ抽出したいんですっけ?
基本的にオブジェクトはキーが重複する事はありえないので、one, twoがそれぞれ複数キーを所持しており、
それらをマージして重複キーが出来たと仮定しましょう。

js

1const result1 = ["smith", "john"]; 2const result2 = [...result1, ...result1]; 3 4console.log(result2); 5// ["smith", "john", "smith", "john"] 6 7result = _.uniq(result2); 8// ["smith", "john"]

【おまけ】 ネイティブのJSだけでやる

イディオムが増えるのでLodashに比べるとかなり可読性が落ちます。
one配下にtwoとthreeのキーが存在しており、
重複キーを取り除きつつ結果を作る想定でやっています。

js

1const obj = { 2 one:{ 3 two:{ 4 smith:{ a:true, b:false }, 5 john :{ a:true, b:true, c:true }, 6 }, 7 three:{ 8 smith:{ a:true, b:false }, 9 will :{ a:true, b:true, c:true }, 10 } 11 }, 12}; 13const result = Object.values(obj.one) 14 .map(its => Object 15 .entries(its) 16 .filter(([key, val]) => 17 Object.values(val).includes(true) 18 ) 19 .map(([key]) => key) 20 ) 21 .reduce((arr, it) => [...arr, ...it]) 22 .reduce((arr, it) => { 23 if (!arr.includes(it)) arr.push(it); 24 return arr; 25 }, []); 26 27console.log(result); 28// ["smith", "john", "will"]

.reduce((arr, it) => [...arr, ...it])

2次元配列を1次元配列にするイディオム

.reduce(arr, it) => {if (!arr.includes(it)) arr.push(it);...

1次元配列からユニークなものだけ取り出すイディオム
(個人的にfilterの方法は好きではないだけ)

投稿2020/01/16 02:51

miyabi-sun

総合スコア21158

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

こんにちは

まず、ご質問に挙げられているコードに最小限の修正をして、 trues に重複が含まれないようにするコードを挙げます。その後、別案を追記します。

現状の問題点は、Object.values(menu) で得られるbool値の配列を調べるときに、 forEach ですべての要素を調べて、true が見つかる度に、truesname を push していることです。これだと、john には true のプロパティが3つあるので、trues に john が3個追加されることになってしまいます。

これを避けるには、Object.values(menu) の要素を先頭から調べていくときに、どこかで true が見つかって nametrues にpushしたら、その時点でループを抜けてその後の要素はチェックしないようにすればよいので、forEachの替わりに some を使って、以下のようにします。

javascript

1let trues = []; 2const two = obj.one.two; 3Object.keys(two).forEach(function(name){ 4 const menu = two[name]; 5 Object.values(menu).some(function(state){ // 修正点① forEach を some に修正 6 if(state===true){ 7 trues.push(name) 8 } 9 return state // 修正点② この行を追加 10 }); 11});

追記1

配列やオブジェクトの操作で便利なライブラリ lodash を使ったコードを挙げます。

まず、オブジェクト e を引数にとり、このe のプロパティの中に、ひとつでも truthy なものがあれば true を返し、ひとつもtruthyなものが無ければfalseを返す関数 checkを作っておきます。先のコードでも使った some を使います。

javascript

1var check = e => Object.values(e).some(flag => flag);

上記の check を使って、 obj.one.twoが以下

javascript

1var obj = { 2 one:{ 3 two:{ 4 smith: { a: true, b: false }, 5 john: { a: true, b: true, c: true }, 6 tom: { a: false, c: false } 7 } 8 } 9}

であった場合に、tomを除いたオブジェクト

javascript

1{ 2 smith: { a: true, b: false }, 3 john: { a: true, b: true, c: true }, 4}

を得るために、 lodashの _.pickBy を使います。

javascript

1var pickedTwo = _.pickBy(obj.one.two, check);

最後に、resultとして、上記のpickedTwo のキーの配列を取得します。

javascript

1var result = Object.keys(pickedTwo);

追記2

lodash を使わなくても、以下でresultを得られます。(checkは、上記の追記1で作成したものと同じです)

javascript

1var { two } = obj.one; 2 3var result = Object.keys(two).filter(name => check(two[name]));

以上、参考になれば幸いです。

投稿2020/01/16 00:26

編集2020/01/16 15:24
jun68ykt

総合スコア9058

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

再帰処理ですべてデータを抽出するとよいのでは?

投稿2020/01/16 01:19

yambejp

総合スコア114829

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yambejp

2020/01/16 01:21

var obj = { one:{ two:{ smith:{ a:true, b:false }, john :{ a:true, b:true, c:true }, andy :{ a:false, b:false}, roger:{}, }, three:{ a:true}, }, } const getAllValuesWithKey=(obj,key)=>{ var ret=[]; Object.entries(obj).forEach(ent=>{ if(ent[1] instanceof Object){ ret=ret.concat(getAllValuesWithKey(ent[1],ent[0])); }else{ ret.push(ent.concat([key])); } }); return ret; }; var res=getAllValuesWithKey(obj); console.log(res.filter(x=>x[1]==true).map(x=>x[2]).filter((x,y,z)=>z.indexOf(x)==y));
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問