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

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

ただいまの
回答率

87.35%

配列での重複確認と重複したものを格納していく処理

解決済

回答 3

投稿

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

score 5

前提・実現したいこと

下記の配列で重複チェックを行い処理を行いたいのですが、

let Records = [
    {
        A: { type: "DROP_DOWN", value: null },
        B: { type: "NUMBER", value: "100" },
        C: { type: "DROP_DOWN", value: null },
        D: { type: "DROP_DOWN", value: "TEST1" },
        E: { type: "SINGLE_LINE_TEXT", value: "" },
        F: { type: "SINGLE_LINE_TEXT", value: "TEST2" },
        G: { type: "SINGLE_LINE_TEXT", value: "5" },
        H: { type: "DATE", value: "2019-09-01" },
        I: { type: "DROP_DOWN", value: "TEST3" },
        J: { type: "NUMBER", value: "10000" },
        K: { type: "SINGLE_LINE_TEXT", value: "001" }
    },
    {
        A: { type: "DROP_DOWN", value: null },
        B: { type: "NUMBER", value: "10000" },
        C: { type: "DROP_DOWN", value: null },
        D: { type: "DROP_DOWN", value: "TEST4" },
        E: { type: "SINGLE_LINE_TEXT", value: "" },
        F: { type: "SINGLE_LINE_TEXT", value: "TEST5" },
        G: { type: "SINGLE_LINE_TEXT", value: "5" },
        H: { type: "DATE", value: "2019-09-01" },
        I: { type: "DROP_DOWN", value: "TEST6" },
        J: { type: "NUMBER", value: "10000" },
        K: { type: "SINGLE_LINE_TEXT", value: "002" }
    },
    {
        A: { type: "DROP_DOWN", value: null },
        B: { type: "NUMBER", value: "1000" },
        C: { type: "DROP_DOWN", value: null },
        D: { type: "DROP_DOWN", value: "TEST7" },
        E: { type: "SINGLE_LINE_TEXT", value: "" },
        F: { type: "SINGLE_LINE_TEXT", value: "TEST8" },
        G: { type: "SINGLE_LINE_TEXT", value: "5" },
        H: { type: "DATE", value: "2019-09-01" },
        I: { type: "DROP_DOWN", value: "TEST9" },
        J: { type: "NUMBER", value: "10000" },
        K: { type: "SINGLE_LINE_TEXT", value: "001" }
    }
];

①重複しているものを除いて抽出(Kのvalue値で重複チェックする)
②その後、重複したものは①のLを追加し配列に格納していく

といった方法をとりたく、最終的には下記の実現を望んでいます。

let Records = [
    {
        A: { type: "DROP_DOWN", value: null },
        B: { type: "NUMBER", value: "100" },
        C: { type: "DROP_DOWN", value: null },
        D: { type: "DROP_DOWN", value: "TEST1" },
        E: { type: "SINGLE_LINE_TEXT", value: "" },
        F: { type: "SINGLE_LINE_TEXT", value: "TEST2" },
        G: { type: "SINGLE_LINE_TEXT", value: "5" },
        H: { type: "DATE", value: "2019-09-03" },
        I: { type: "DROP_DOWN", value: "TEST3" },
        J: { type: "NUMBER", value: "10000" },
        K: { type: "SINGLE_LINE_TEXT", value: "001" },
     //追加部分
        L: [{
             A: { type: "DROP_DOWN", value: null },
             B: { type: "NUMBER", value: "1000" },
             C: { type: "DROP_DOWN", value: null },
             D: { type: "DROP_DOWN", value: "TEST7" },
             E: { type: "SINGLE_LINE_TEXT", value: "" },
             F: { type: "SINGLE_LINE_TEXT", value: "TEST8" },
             G: { type: "SINGLE_LINE_TEXT", value: "5" },
             H: { type: "DATE", value: "2019-09-03" },
             I: { type: "DROP_DOWN", value: "TEST9" },
             J: { type: "NUMBER", value: "10000" },
             K: { type: "SINGLE_LINE_TEXT", value: "001" }
           }]
    },
    {
        A: { type: "DROP_DOWN", value: null },
        B: { type: "NUMBER", value: "10000" },
        C: { type: "DROP_DOWN", value: null },
        D: { type: "DROP_DOWN", value: "TEST4" },
        E: { type: "SINGLE_LINE_TEXT", value: "" },
        F: { type: "SINGLE_LINE_TEXT", value: "TEST5" },
        G: { type: "SINGLE_LINE_TEXT", value: "5" },
        H: { type: "DATE", value: "2019-09-03" },
        I: { type: "DROP_DOWN", value: "TEST6" },
        J: { type: "NUMBER", value: "10000" },
        K: { type: "SINGLE_LINE_TEXT", value: "002" },
    }
];

試したこと

let results = Records.reduce((res, cur) => {
    for (let i = 0, s = res.length, name = cur.K.value, object; i < s; ++i) {
        object = res[i];
        if (object.K.value === name) {return res;}
    }
    res.push(cur);
    return res;
}, []);


これで、上記の①の部分は出来ていると思うのですが②の部分をどの様にしていけば良いか悩んでいます。ご教授頂けましたら幸いです。

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

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

  • Lhankor_Mhy

    2019/09/03 13:09

    補足願います。

    {
    A: { type: "DROP_DOWN", value: null },
    B: { type: "NUMBER", value: "100" },
    ...
    L: [{
    A: { type: "DROP_DOWN", value: null },
    B: { type: "NUMBER", value: "1000" },
    ...
    }]
    },

    ↑これは、↓これでもいいのですか?

    {
    A: { type: "DROP_DOWN", value: null },
    B: { type: "NUMBER", value: "1000" },
    ...
    L: [{
    A: { type: "DROP_DOWN", value: null },
    B: { type: "NUMBER", value: "100" },
    ...
    }]
    },

    つまり、Lを増やすオブジェクトと、増やしたLに入れるオブジェクトとは、ご提示の手順ですと区別がつかないようですが、それらの関係は任意で構いませんか?

    キャンセル

  • jun68ykt

    2019/09/03 13:13

    処理の一部に外部のライブラリを使うことは、回答としてアリでしょうか?

    キャンセル

  • jun68ykt

    2019/09/04 00:52

    先の当方からのご質問、
    > 処理の一部に外部のライブラリを使うことは、回答としてアリでしょうか?
    にまだご返答頂いておりませんが、Lodash の groupBy を使うとRecordsから最終的に欲しいresuls を得るコードを手短かに書けるので、参考までに、これを使ったコード例もあわせて回答しました。

    キャンセル

回答 3

+1

KとL以外関係なさそうなのでほかは省略してAだけダミーを置きました

let Records = [
  {A:1, K: { type: "SINGLE_LINE_TEXT", value: "001" }},
  {A:2, K: { type: "SINGLE_LINE_TEXT", value: "002" }},
  {A:3, K: { type: "SINGLE_LINE_TEXT", value: "001" }},
  {A:4, K: { type: "SINGLE_LINE_TEXT", value: "002" }},
  {A:5, K: { type: "SINGLE_LINE_TEXT", value: "003" }},
  {A:6, K: { type: "SINGLE_LINE_TEXT", value: "002" }},
];
var rec={};
Records.forEach((x,y)=>{
  var k=JSON.stringify(x.K);
  if(typeof rec[k]=="undefined") rec[k]=[];
  rec[k].push(y);
});
for(var i=Records.length-1;i>=0;i--){
  var k=JSON.stringify(Records[i].K);
  var r=rec[k][0];
  if(r!==i){
    if(typeof Records[r].L=="undefined") Records[r].L=[];
    Records[r].L.push(Records.splice(i,1));
  }
}
</script>

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2019/09/04 13:48 編集

    返信大変遅くなりまして、申し訳ありません。
    また、本件に対しご貴重なお時間を費やしてくださり、お答え頂きとても感謝致します。

    内容全て見れておりませんが、勉強させて頂きます。ありがとうございます。

    キャンセル

checkベストアンサー

0

こんにちは

この回答では、以下の(1)、(2)、(3)の3つのコードを示します。

(1) ご質問で問われている②のコード例

ご質問に

上記の①の部分は出来ていると思うのですが②の部分をどの様にしていけば良いか悩んでいます。 

とありましたので、ご質問に挙げられている、①の部分のコードには手を加えず、①に続く②の部分のコード例を挙げます。

(2) Lodash を使ったコード例

配列の操作で便利なメソッドを提供するライブラリ Lodash を使って、Records から直接、最終的に欲しい形で results を得るコードを挙げます。

 (3)ご質問に挙げられている①の部分のコードを修正したもの 

ご質問に挙げられている、①の部分のコードを修正して、①を行いながら②も併せて行うようにします。(この回答の最後に、追記2 として記載しています)

(1) ご質問で問われている②のコード例

以下に処理の概要を示します。

  1. Records の要素を、先頭から最後までのループで取得。各要素を e、そのインデクスを iとする。
  2. 処理①によって得られたresults の要素oの中で、 o.K.value の値が e.K.value と等しい要素をみつけ、これを objとする。
  3. もし obj.L が undefined であれば 空の配列を入れておく。
  4. Records の要素oの中で、 o.K.value の値が e.K.valueと等しいもののうち、最も先頭に近い要素のインデクスを index とする。
  5. i が index よりも大きければ、 e を obj.Lに追加する。
  6. また、上記で、2. と 4. の検索を、毎回行うのは無駄なので、一度、objと index を取得した e.K.value については、再度の検索を不要にするために、適当なオブジェクトにキャッシュしておく。

以下は、上記をコードにしたものです。ご質問にある①のコードに続く、②だけのコードです。

const cache = {};  // 上記 6. のキャッシュオブジェクト

Records.forEach((e, i) => {
  const { value } = e.K;
  let info = cache[value]; // キャッシュにすでに情報があれば取得
  if (!info) { // キャッシュにない場合、
    const obj = results.find(o => o.K.value === value);  // 上記 2. の検索
    obj.L = [];  // ここでは、obj.L は undefined なので空配列を入れておく。
    const index = Records.findIndex(o => o.K.value === value); // 上記 4. の検索
    info = { obj, index }; // キャッシュするオブジェクトを作り、
    cache[value]  = info;  // value をキーにして、 obj と index を入れておく。
  }
  if (i > info.index)  // 上記、 5. の処理
    info.obj.L.push(e); 
});

上記のコードを試すために、Codepen に動作確認できるものを作成しました。

なお、上記のサンプルでは

  • 処理 ① の部分はご質問に挙げられているコードそのままです。
  • 動作確認をしやすくするために、 Records の各要素は、ユニークな id プロパティを持ち、 K プロパティは valueのみを持つオブジェクトとし、 id と K以外は省略して、以下のようにしました。
let Records = [
  { id: 0, K: { value: "001" } },
  { id: 1, K: { value: "002" } },
  { id: 2, K: { value: "001" } },
  { id: 3, K: { value: "003" } },
  { id: 4, K: { value: "001" } },
  { id: 5, K: { value: "003" } },
  { id: 6, K: { value: "004" } },
  { id: 7, K: { value: "001" } },
  { id: 8, K: { value: "003" } },
  { id: 9, K: { value: "002" } }
];

(2) Lodash を使ったコード例

配列の操作で便利なメソッドを提供するライブラリ Lodash に _.groupBy というメソッドがあります。このメソッドは、配列の各要素を、要素に対する何らかの関数による値をキーとしてグルーピングしたオブジェクトを返してくれます。

今回のご質問の場合、Records の 各要素 e について、 e.K.value の値でグルーピングすればよいので、以下のようにします。

_.groupBy(Records, e => e.K.value)

先ほどの動作確認用サンプルに使った Records を与えた場合、上記の _.groupBy によって返されるオブジェクトは、以下となります。

{ 
  '001':[ 
     { id: 0, K: { value: "001" } },
     { id: 2, K: { value: "001" } },
     { id: 4, K: { value: "001" } },
     { id: 7, K: { value: "001" } }
  ],
  '002': [ 
     { id: 1, K: { value: "002" } },
     { id: 9, K: { value: "002" } }
  ],
  '003':[ 
     { id: 3, K: { value: "003" } },
     { id: 5, K: { value: "003" } },
     { id: 8, K: { value: "003" } }
  ],
  '004': [ 
     { id: 6, K: { value: "004" } } 
  ]
}


この _.groupBy で返されるオブジェクトを使って、ご質問に書かれている要件のresults を得るには、以下のようにします。

const results = Object.values(
                  _.groupBy(Records, e => e.K.value)
                ).map(ary => ({ ...ary[0], L: ary.slice(1) }));

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

追記1

上記の回答で、 (2) に挙げたコードでは、一度グルーピングされたオブジェクトを作り、それに対して、Object.values() で配列を作り、その配列に対する .map() で results を作っています。ですので、オブジェクトのエントリに順序はないことから、元のRecords の中での異なるvalue の出現順が、 results でも保たれる保証はないです。

もし、 Records での異なる valueの出現順が、 results での value の順番として保たれるようにするには、以下のようにします。

// Recordsの要素に出現する value を、出現順に、かつ、重複を除いた配列を得る。
const orderedValues = Records
                        .map(e => e.K.value)
                        .filter(
                          (v, i, values) => i === values.findIndex(v2 => v2 === v)
                        );

// value の値でグルーピングしたオブジェクトを得る。
const groupsByValue = _.groupBy(Records, e => e.K.value);

// orderedValues と groupsByValue から results を得る。
const results = orderedValues.map(v => ({
                  ...groupsByValue[v][0],
                  L: groupsByValue[v].slice(1)
                }));

追記2

 (3)ご質問に挙げられている①の部分のコードを修正したもの 

以下のように、ご質問に挙げられている、①の部分のコードを少し修正して、②の処理も併せて行うようにすることもできます。

let results = Records.reduce((res, cur) => {
    for (let i = 0, s = res.length, name = cur.K.value, object; i < s; ++i) {
        object = res[i];
-       if (object.K.value === name) {return res;}
+       if (object.K.value === name) {
+           object.L.push(cur);
+           return res;
+       }
    }
-    res.push(cur);
+    res.push({...cur, L:[] });
    return res;
}, []);

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2019/09/04 13:48 編集

    返信大変遅くなりまして、申し訳ありません。
    また、本件に対しご貴重なお時間を費やしてくださり、お答え頂きとても感謝致します。

    内容全て見れておりませんが、勉強させて頂きます。ありがとうございます。

    キャンセル

  • 2019/09/04 13:51

    どういたしまして。お役に立てましたら幸いです。

    キャンセル

0

重複したレコードがあれば、Lというプロパティが存在しないオブジェクトを見つけ、Lというプロパティに空の配列をセットし、そこにcurpushすれば良いです。

let results = Records.reduce((res, cur) => {
    let duplicatedRecord = res.find(v => v.K.value === cur.K.value);
    if (duplicatedRecord) {
        if(!duplicatedRecord.L) duplicatedRecord.L = [];
        duplicatedRecord.L.push(cur);
    } else {
        res.push(cur);
    }
    return res;
}, []);

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2019/09/03 13:20 編集

    13:23以前のコードだとLが配列ではないですね。要件通りに直しました。

    キャンセル

  • 2019/09/04 13:49 編集

    返信大変遅くなりまして、申し訳ありません。
    また、本件に対しご貴重なお時間を費やしてくださり、お答え頂きとても感謝致します。

    内容全て見れておりませんが、勉強させて頂きます。ありがとうございます。

    キャンセル

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

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

関連した質問

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