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

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

ただいまの
回答率

87.37%

多重配列の重複した部分を分類したい

解決済

回答 3

投稿

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

score 24

発生している問題、目指している形

ソースコード(js)はテキストを読み込み、細かく分けて配列にしているものです。

例えばソースコード(txt)を読み込み
result4=[
[ [a,1], [b,2], [c,x], [d,y] ],
[ [a,y], [b,x], [c,2], [d,1] ]
]
の様な多重配列があるとき、一番小さい配列の2番目の文字や数字(この場合は1,2,x,y)が同じものを分類したいです。
つまり、result4[0][0]の[a,1]とresult4[1][3]の[d,1]、同様に[c,x]と[b,x]のような配列をすべて分類したいです。

調べたところ1重の配列の分類方法は出てきたのですが、多重ではどうすればよいか分かりませんでした。
ご教授お願いします。

該当のソースコード

<body>
<form name="test">
<input type="file" id="selfile"><br>
<textarea name="txt" rows="10" cols="60" readonly></textarea>
</form>

<script>

var obj1 = document.getElementById("selfile");

obj1.addEventListener("change",function(evt){

  var file = evt.target.files;
  var reader = new FileReader();
  reader.readAsText(file[0]);

  reader.onload = function(ev){

    //テキストエリアに表示
    document.test.txt.value = reader.result.toLowerCase();
    var result2=reader.result.toLowerCase().split("\n");

    var result3=[];
    for (var i = 0; i <result2.length; i++) {         
        result3.push(result2[i].split(/\s+/));
    }


    for (var i = 0; i<result3.length; i++) {  
        if(result3[i].length>=5){
          while (result3[i].length>=5){
            result3[i].pop();
          }
        }
    }

    Array.prototype.divide = function(n){
    var ary = this;
    var idx = 0;
    var results = [];
    var length = ary.length;

    while (idx + n < length){
        var result = ary.slice(idx,idx+n)
        results.push(result);
        idx = idx + n
    }
    var rest = ary.slice(idx,length+1)
    results.push(rest)
    return results;
    }

    result4=[];
    for (var i = 0; i<result3.length; i++) {  
      result4.push(result3[i].divide(1));
    }

    for (var i = 0; i<result4.length; i++) {  
          result4[i][0].unshift("a"); 
          result4[i][1].unshift("b"); 
          result4[i][2].unshift("c"); 
          result4[i][3].unshift("d"); 
    }
console.log(result4);
  }
},false);
</script>
</body>
1 2 x y
y x 2 1
  • 気になる質問をクリップする

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

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

  • think49

    2019/08/02 08:13

    「分類」とは、どういう結果ですか。
    「1重の配列の分類方法」のURLを開示できますか。

    キャンセル

  • jsrookie

    2019/08/02 08:58

    分かりづらい質問で申し訳ございません。
    https://qiita.com/cocottejs/items/7afe6d5f27ee7c36c61f
    のduplicate_check.jsのcのようなものを多重配列でさらに2と3に配列を分けたいと考えていました。

    キャンセル

  • think49

    2019/08/02 12:29

    > duplicate_check.jsのc
    「非重複な要素を削除する」ということですか。質問文の要件を修正して下さい。

    キャンセル

回答 3

checkベストアンサー

0

こんにちは

多重ではどうすればよいか分かりませんでした。

とのことですが、

多重

とは、具体的にはどのような階層構造の中に目的の配列があるのかについて、ご質問からは読み取れませんでしたので、全体の配列の中のどこにあっても、長さが2で要素が文字列か数字であるような

一番小さい配列

を拾い出すコードを回答します。なお、以下では、この

一番小さい配列

のことを minimal な配列 と呼び、 minimal な配列の先頭要素を head, 2番目の要素を tail と呼ぶことにして、これらを変数名やプロパティ名、関数名に使っています。

index.html

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>Q203923</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.min.js"></script>
    <script src="./assort.js"></script>
    <script>
      function main() {
        var obj1 = document.getElementById("selfile");

        obj1.addEventListener("change",function(evt) {

          var file = evt.target.files;
          var reader = new FileReader();
          reader.readAsText(file[0]);

          reader.onload = function (ev) {
            var text = reader.result.toLowerCase();
            document.test.txt.value = text;

            var result = assort(text);

            console.log(result);
          }
        });
      }

      document.addEventListener('DOMContentLoaded', main);
    </script>
</head>
<body>
<form name="test">
    <input type="file" id="selfile"><br>
    <textarea name="txt" rows="10" cols="60" readonly></textarea>
</form>
</body>
</html>

上記で、選ばれたファイルの内容を受け取って、結果を返す assort という関数を作りました。関数 assort は、以下のファイル assort.js の最後にあります。minimal な配列を収集した後、重複を除くための処理に、 lodash の uniqBy を使っています。

assort.js

const isMinimal = a => (
  Array.isArray(a) &&
  a.length === 2 &&
  [typeof a[0], typeof a[1]].every(
    t => ['number', 'string'].includes(t)
  )
);

const getMinimals = (a, result=[]) => {
  if (isMinimal(a))
    result.push(a);
  else if (Array.isArray(a))
    a.forEach(e => { getMinimals(e, result); })

  return result;
}

const compare = (e1, e2) => {
  const type1 = typeof e1;
  const type2 = typeof e2;

  if (type1 !== type2) {
    return type1 === 'number' ? -1 : 1
  } else {
    return type1 === 'number' ? e1 - e2 : e1.localeCompare(e2)
  }
};

const getKeys = ary =>
  ['heads', 'tails'].reduce((m, k, i) =>
    m.set(k, _.uniq(ary.map(e => e[i])).sort(compare))
  , new Map());

const assortByKeys = (keys, ary) => {
  const heads = keys.get('heads').map(k => ({
        value: k,
        minimalArrays: ary.filter(e => e[0] === k)
  }));

  const tails = keys.get('tails').map(k => ({
    value: k,
    minimalArrays: ary.filter(e => e[1] === k)
  }));

  return { heads, tails }
}

const assort = text => {

  // 先頭の '変数名=' を除去
  text = text.replace(/^(.+)=/, '')

  // 配列要素として出現する変数名をダブルコーテーションで囲む。
  const jsonStr = text.replace(/([a-z][a-z0-9]*)/g, name => `"${name}"`);

  // JSON文字列としてパース
  const data = JSON.parse(jsonStr);

  // 最小配列(要素の数が2個で、要素が数または文字列の配列) を収集
  const minimals = _.uniqBy(getMinimals(data), e => e.join(','))

  // 分類キーを作成
  const keys = getKeys(minimals)

  // 最小配列を分類する
  const result = assortByKeys(keys, minimals)

  return result;
}

上記によって、以下のテキストファイル

input.txt

result4=[
[ [d,1], [b,2], [c,x], [d,y] ],
[ [a,y], [b,x], [c,2], [d,1] ]
]


を読むと、result には以下のようなオブジェクトが入ります。

{
  heads: [
    { value: "a", minimalArrays: [["a","y"]]},
    { value: "b", minimalArrays: [["b",2],["b","x"]]},
    { value: "c", minimalArrays: [["c","x"],["c",2]]},
    { value: "d", minimalArrays: [["d",1],["d","y"]]}
  ],
  tails: [
    { value: 1, minimalArrays: [["d",1]]},
    { value: 2, minimalArrays: [["b",2],["c",2]]},
    { value: "x", minimalArrays: [["c","x"],["b","x"]]},
    { value: "y", minimalArrays: [["d","y"],["a","y"]]}
  ]
}       

また、以下のような入力

input2.txt

result5=[

  [
    [a,1],[b,5],[ [d,8], [c,3], [a,7], [c,5] ]
  ],

  [
    [[ [d,8], [a,1] ], [c, 6], [[[  [x,y], [x,z], [x,5] ]]] ],
    [f,6], [ [d, 7], [c, 5] ]
  ]

]


に対しても、以下が得られます。

{
  heads: [
    { value: "a", minimalArrays: [["a",1],["a",7]]},
    { value: "b", minimalArrays: [["b",5]]},
    { value: "c", minimalArrays: [["c",3],["c",5],["c",6]]},
    { value: "d", minimalArrays: [["d",8],["d",7]]},
    { value: "f", minimalArrays: [["f",6]]},
    { value: "x", minimalArrays: [["x","y"],["x","z"],["x",5]]}
  ],
  tails:[
    { value: 1, minimalArrays: [["a",1]]},
    { value: 3, minimalArrays: [["c",3]]},
    { value: 5, minimalArrays: [["b",5],["c",5],["x",5]]},
    { value: 6, minimalArrays: [["c",6],["f",6]]},
    { value: 7, minimalArrays: [["a",7],["d",7]]},
    { value: 8, minimalArrays: [["d",8]]}, 
    { value: "y", minimalArrays: [["x","y"]]},
    { value: "z", minimalArrays: [["x","z"]]}
  ]
}

上記の回答に書いたコードでは、

  • getMinimals が再帰関数になっているので、想定していない入力があると無限ループになる恐れがありますので、実用化するには何らかの対策が必要

  • 一度、すべての minimal な配列を拾い出してから分類しているので、やや冗長なコードになっています。

といった課題が残りますが、各論はさておき、上記のコードで示したいことは、

  1. 入力されたテキストを加工してJSONの配列にする。
  2. JSONをパースして、JavaScript の配列を得る。
  3. 得られた配列の要素から、minimalな配列を拾い出すために再帰を使うことができる。
  4. 拾い出した minimalな配列 を分類する。

というステップを踏むのはいかがでしょうか?という考え方です。

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

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2019/08/04 10:59

    こちらのご質問、解決されましたでしょうか?

    ご質問と他の回答者さまからの回答も含め、回答を見直しましたが、もしかすると私の回答はまったく jsrookieさんの問題解決にはなっていなかったかもしれないと思っています。というのは、私の回答は、ご質問の意図として、 <input type="file" > で読み込むテキストファイルの内容が

    result4=[
    [ [d,1], [b,2], [c,x], [d,y] ],
    [ [a,y], [b,x], [c,2], [d,1] ]
    ]


    というものだと解釈した回答になっていますが、この解釈自体、ご質問を誤読していたかもしれません。その場合は申し訳なかったです。

    キャンセル

  • 2019/08/06 13:25

    ここ数日多忙で返事が遅くなってしまいました。
    申し訳ございません。
    JSONはまだあまり触れていないので今後疑問点が出た際は何卒よろしくお願いいたします。
    また、読み込むファイルは配列ではなく
    1 2 x y
    y x 2 1
    のような文字間に改行やスペースを入れたようなものなのですが、読み込んだ文字列はプログラム上でresult4のような配列にする予定ですのでjun68ykt様の回答は今後改良していく上で活かしていきたいと考えています。
    ありがとうございました。

    キャンセル

  • 2019/08/06 20:41

    ご返信ありがとうございます。多少なりともお役に立てましたようで、何よりです。

    キャンセル

0

一次元にバラしてから処理するのはどうですか。
https://qiita.com/Toyoharu-Nishikawa/items/5faa1834926d67e5ac04

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2019/08/02 05:04

    回答ありがとうございます。
    一応[a,1]などのa, b, c, dも意味のある情報なので1つの配列内に残しておきたいのですが、1次元にバラシてもa,b,c,dも一緒に分類できるでしょうか?

    キャンセル

  • 2019/08/03 05:30

    あ、すいません。
    ↓のとこに回答を書いちゃいました・・・

    キャンセル

0

連想配列でだいじょうぶでしょうか

var result5 = [];
for (var i = 0; i < result4.length; i++) {         
    for (var j = 0; j < result4[i].length; j++) {         
        k = result4[i][j][1];
        if (!result5[k]) {
            result5[k] = [];
        }
        result5[k].push(result4[i][j]);
    }
}

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2019/08/02 08:47

    回答ありがとうございます!
    現在は教えて下さったプログラムを動かせる状況にないのですが、内容を読んだところ私の求めていたものにかなり近いと感じます。
    動作が確認でき次第改めてお返事をさせていただきます。
    本当にありがとうございます!

    キャンセル

  • 2019/08/02 11:53

    こんな感じに変更するという意味です。
    これではマズいでしょうか。
    result4=[
    [ [a,1], [b,2], [c,x], [d,y] ],
    [ [a,y], [b,x], [c,2], [d,1] ]
    ]

    [a,1], [b,2], [c,x], [d,y],
    [a,y], [b,x], [c,2], [d,1]

    キャンセル

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

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

関連した質問

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