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

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

ただいまの
回答率

87.37%

jQeuryオブジェクト抽出で、子階層の場合の処理について

解決済

回答 1

投稿

  • 評価
  • クリップ 0
  • VIEW 789

score 12

実現したいこと

オブジェクトを抽出するとき、親階層と子階層を、同じ関数get_extractObjで抽出したいです。

対象のオブジェクト

ご覧のように親階層にも子階層にも"post_type":"media"があり、これらをうまく取得する方法を模索しています。

/*
* 対象のオブジェクトを取得
***************************************************/
function getObj(){
    var obj = {
        "obj":
        {
          "library": 
          [
              {"area":"library","post_type":"media","media_id":"10"},
              {"area":"library","post_type":"comment","comment_id":"100"}
          ],
          "timeline":
          [
            {"area":"timeline","post_type":"comment","comment_id":"100"},
            {"area":"timeline","post_type":"comment","comment_id":"200",
                "attached":
                {
                    "media":[
                        {"area":"timeline","post_type":"media","media_id":"10"}
                    ]      
                }
            },
            {"area":"timeline","post_type":"comment","comment_id":"300",
                "attached":
                {
                    "media":[
                      {"area":"timeline","post_type":"media","media_id":"10"},
                      {"area":"timeline","post_type":"media","media_id":"20"}
                    ]  
                }
            }    
          ]
        }
    }
    return obj;
}

試したこと・不安

こちらのコードでいちおう出来てはいるものの、抽出の流れに不安がございます。
https://jsfiddle.net/xw6e8gb7/

関数get_extractObjは、targetObjからtargetIdのオブジェクトを抽出するという機能です。

抽出の対象が親階層の場合は問題ないと思います。

しかし子階層の場合は、まず親オブジェクトの取得を経る次の➀➁➂の流れになっていまして、これが無駄なのではないか?と不安に思いました。

get_extractObj()の引数にparentIdparentTypeという親情報を渡し、
➁そこからparentObjという親オブジェクトを取得し、
➂その中から改めてextractObjという目的のオブジェクトを抽出する。

質問

親情報を渡したり親オブジェクトの取得を経なくても、extractObjを抽出する方法があるのではないか?と思い、その方が引数が減りますし、処理も早くなりそう(?)なので質問させて頂きました。

どなたかお詳しい方がいらっしゃいましたら、どうぞ宜しくお願い致します。

jsfiddleのコード

先述したjsfiddleのコードになります。

/*
* target_objから、target_idのオブジェクトを抽出
***************************************************/
function get_extractObj( targetObj, area, targetType, targetId, parentType=null, parentId=null ){

    let extractObj;

    const areaObj = targetObj['obj'][area];

    // 親階層について、targetIdのオブジェクトを抽出
    if( parentType==null ){
        extractObj = areaObj.find( e=>e[targetType+'_id'] && e[targetType+'_id'] === targetId );
    }    

    // 子階層について、parentIdのオブジェクトのattachedから、targetIdのオブジェクトを抽出
    if( parentType!=null ){
        const parentObj = areaObj.find( e=>e[parentType+'_id'] && e[parentType+'_id'] === parentId );
        extractObj = parentObj['attached'][targetType].find( e=>e[targetType+'_id'] && e[targetType+'_id'] === targetId );
    }

    // 抽出結果
    return extractObj;            
}


/*
* 実行
***************************************************/
// 親の抽出 ( media )
var result1 = get_extractObj( getObj(), 'library', 'media', '10' );
console.log('result1:',result1);
// 親の抽出 ( comment )
var result2 = get_extractObj( getObj(), 'library', 'comment', '100' );
console.log('result2:',result2);
// 子の抽出 ( media )
var result3 = get_extractObj( getObj(), 'timeline', 'media', '10', 'comment', '200' );
console.log('result3:',result3);
  • 気になる質問をクリップする

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • jun68ykt

    2019/09/29 14:33

    ご回答ありがとうございます。いえ。no problem です。
    なるほどです。
    それをふまえて、もう一点確認ですが、ご質問にあるコードにある getObj() で得られるオブジェクトを対象にして、

    var result3 = get_extractObj( getObj(), 'timeline', 'media', '10');

    とすると、media_id が "10" のオブジェクトは、 以下の2カ所にあります。

    1) comment_id : 200 のオブジェクトの attached.media の中
    2) comment_id : 300 のオブジェクトの attached.media の中

    この場合、get_extractObj が返すオブジェクトとしては、上記 1) と 2) のいずれにするのか?について何か要件はありますでしょうか?
    または、どちらを返してもかまわない( = get_extractObj のアルゴリズムの中で、先に見つかった方を返す、など)のでしょうか?

    キャンセル

  • kuroean

    2019/09/29 14:51

    あう、そうですよね。そこが抜けてました。先に見つかった方が得られればと思っています。2か所にあっても必ず同じデータになっているはずだからです。ということで、 1) と 2) についての指定はありません。
    また何かございましたがいつでもご指摘くださいませ。

    キャンセル

  • jun68ykt

    2019/09/29 16:40

    ご返信ありがとうございます。回答しましたので、参考になれば幸いです。(P.S. find はご自身のモノにしたんですね。あと => もお使いになっており、コードが進歩しているのを感じます)

    キャンセル

回答 1

checkベストアンサー

+1

こんにちは

回答するにあたって、親と子の(最大)2階層に制限するのは汎用性がないと思われましたので、子もまた子を持っているかもしれないという構造を対象に考えました。そうすると、対象のオブジェクトの各area は、.attached.xxx によって子要素の配列を持つノードのツリー(木構造) として把握できます。ツリーのノードの中で、何らかの条件に合致するものを探すロジックは、再帰 関数を使えば書けます。

以下は get_extractObj の修正版です。 _recursive_search という再帰関数を作り、これを利用して該当する要素を見つけ出すコードです。

function get_extractObj( targetObj, area, targetType, targetId) {

  let extractObj;

  const areaArray = targetObj['obj'][area];

  const _recursive_search = (ary) => {
    if (!ary || ary.length === 0) return false;

    return ary.some(e => {
      if (e[`${targetType}_id`] === targetId) {
        extractObj = e;
        return true;
      } else {
        const children = e.attached && e.attached[targetType]
        return _recursive_search(children);
      }
    });
  }

  _recursive_search(areaArray)

  // 抽出結果
  return extractObj;
}

なお、上記のCodePenのサンプルでは、media_id が "10" の要素に、uniqId というプロパティを追加して、複数該当する場合にどれが該当オブジェクトとして取得されたかが分かるようにしています。

以下のサンプルは、該当する要素をもう1つ加えたものです。

上記では、comment_id が "100" の要素からみると孫(子の子)にあたる深さの場所にmedia_id が "10" の要素がありますが、これをget_extractObj は最初にみつけて extractObj に代入し、探索を終了します。このように、子要素があれば、それらを深掘りして探すほうを優先させる探し方を、深さ優先探索 といいます。

※ 実際の業務で、再帰関数を作る場合、無限ループになったりしないように、何らか再帰をストップさせる条件を明示的に追加するなどの注意が必要です。(たとえば、再帰呼び出しの深さが10になったら、それより深いところは探索させないようにする、など)

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/09/29 18:02

    深さ優先探索ですか。まさにその概念を求めていました。ありがとうございます。そして相変わらずのクオリティ!!脱帽です。汎用性についてのお気遣いも完璧で、階層の深さへのご対応や、取得元のuniqIdを付けるというアイディアも恐れ入りました。いやぁもう…誠にありがとうございます。

    キャンセル

  • 2019/09/29 21:52

    どういたしまして。こちらこそ、毎回私の、いろいろ脱線しがちな、アレもコレも詰め込もうとする回答に、丁寧なご返事を頂き、ありがとうございます。益々の上達を祈念しております。草々

    キャンセル

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

  • ただいまの回答率 87.37%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る