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

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

ただいまの
回答率

90.46%

  • JavaScript

    17022questions

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

JavaScriptでかぶらないように変数を分配したい

受付中

回答 5

投稿

  • 評価
  • クリップ 1
  • VIEW 1,784

Furisuke

score 93

3種類の文字列を入れた配列を2つ用意し、3つに分配する際に
・絶対にかぶってはいけない
・各文字列2つまでなので
ジュースとアメ
ジュースとチョコ
ジュースとアメ
というのは不可能

こういったものを書くにはどこを改善すればいいのでしょうか?

<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <title></title>
</head>
<body>

<script>
  
  var menu1 = ["ジュース","チョコ","アメ"];
  var menu2 = ["ジュース","チョコ","アメ"];

  for(var i = 0; i < 3; i++){
    //menuのrandom
    var rnd1 = Math.floor(Math.random() * menu1.length);
    var rnd2 = Math.floor(Math.random() * menu2.length);
    console.log(menu1[rnd1] + "と" + menu2[rnd2]);
  }

</script>
</body>

</html>
  • 気になる質問をクリップする

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

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

  • katoy

    2015/08/23 17:14

    入力する menu1, menu2 の条件を明確にしてください。 例: 長さはどちらも 3 固定なのか、 固定ではないが、 menu1 と menu2 の長さは同じでなければならないのか、 2つののメニューの長さは無関係でなんでもよいのか? 出力するメニュー 3 つの生成方法と、制約条件を明確にしてください。 2つ以上の正しい生成例を含めてください。2つ以上の不正な生成例を含めてください。それらには不正理由も記載してください。

    キャンセル

  • Furisuke

    2015/08/24 11:11

    ジュース、チョコ、アメの引換券が2枚ずづあるとします それを3人の子どもに均等に分配します その時にジュースとジュース、アメとアメと言った同じものを二枚渡すのはダメだということです

    キャンセル

回答 5

+1

menu1 = ["ジュースA","チョコA","アメA"]
menu2 = ["ジュースB","チョコB","アメB"]
と捉え、
  1.  menu1、menu2から各要素は1度しか取り出されない。
  2.  menu1、menu2の要素の名前ではなく、取り出し順が同じにならない。
という方法で、取得する方法を書いてみます。
※ 2の条件から menu2 = ["チョコB","ジュースB","アメB"] とした場合は
  ジュースA と ジュースB の様に取り出されてしまいます。

各処理をわかり易く書こうと思ったら、
すごく冗長なコードになってしまったので、かなりリファクタリング出来ると思います。
var menu1 = ["ジュースA","チョコA","アメA"];
var menu2 = ["ジュースB","チョコB","アメB"];

// 配列の長さが違うと死ぬので同じにする
var adjustLength = function(arg1, arg2) {
  var l1 = arg1.length,
      l2 = arg2.length,
      gap = Math.abs(l1 - l2),
      adjustArg;
  if(gap) {
    adjustArg = (l1 > l2)? arg2 : arg1;
    for(var i=0; i<gap; i+=1) {
      adjustArg[ adjustArg.length ] = "なし!";
    }
  }
};

// 取り出し方のパターンを作成
var getRandomPattern = function(n) {
  var arg = [],
      patterns = [];
  for(var i=0; i<n; i+=1) {
    arg[i] = i;
  }

  var generatePattern = function(tmp, post, n) {
    if(n > 0) {
      var next, rest;
      for(var i=0, l=post.length; i<l; i+=1) {
        rest = post.slice(0);
        next = rest.splice(i, 1);
        generatePattern(tmp.concat(next), rest, n-1);
      }
    } else {
      console.log(tmp);
      patterns[ patterns.length ] = tmp;
    }
  }
  console.log('▼ 取り出し順の全パターン ▼');
  generatePattern([], arg, arg.length);
  return patterns;
};

!function() {
  var i = 0,
      l = 0,
      patterns,
      rand1,
      rand2,
      ptn1,  // menu1 の取り出し順
      ptn2,  // menu2 の取り出し順
      isSame = true;

  adjustLength(menu1, menu2);
  patterns = getRandomPattern( menu1.length );
  rand1 = Math.floor( Math.random() * patterns.length );
  ptn1 = patterns.splice(rand1, 1)[0];
  while(isSame) {
    rand2 = Math.floor( Math.random() * patterns.length );
    ptn2 = patterns.splice(rand2, 1)[0];
    isSame = false;
    for(i=0, l=ptn1.length; i<l; i+=1) {
      // 取り出し順の値が被らないかチェック
      if(ptn1[i] === ptn2[i]) {
        isSame = true;
        break;
      }
    }
  }
  console.log('▼ menu1, menu2 それぞれの取り出し順 ▼');
  console.log(ptn1, ptn2);

  console.log('▼ 決まった取り出し順に基いて順番に取り出す ▼');
  for(i=0, l=ptn1.length; i<l; i+=1) {
    var m1 = menu1[ ptn1[i] ],
        m2 = menu2[ ptn2[i] ];
    console.log(m1 + 'と' + m2);
  }
}();

要素名で被らないようにするのであれば、下記で上手く取り出せそうな気がします。
  1.  menu1、menu2のそれぞれの並び順のパターンを作成
  2.  ランダムに並び順のパターンを取得
  3.  同じ取り出し順に同じ値があったら2をやり直し
  4.  ループで各配列の並び順に出力

var menuA = ["ジュース","チョコ","アメ"];
var menuB = ["チョコ","ジュース","アメ","バナナ"];

// 配列の長さを同じにする
var adjustLength = function(arg1, arg2) {
  var l1 = arg1.length,
      l2 = arg2.length,
      gap = Math.abs(l1 - l2),
      adjustArg;
  if(gap) {
    adjustArg = (l1 > l2)? arg2:arg1;
    for(var i=0; i<gap; i+=1) {
      adjustArg[ adjustArg.length ] = "なし!";
    }
  }
};

// 配列の並び順のパターンを作成
var generatePatterns = function(arg) {
  var patterns = [];
  var sortArg = function(allPtns, ptnArg, post, n) {
    if(n > 0) {
      var next, rest;
      for(var i=0, l=post.length; i<l; i+=1) {
        rest = post.slice(0);
        next = rest.splice(i, 1);
        sortArg(allPtns, ptnArg.concat(next), rest, n-1);
      }
    } else {
      console.log(ptnArg);
      allPtns[ allPtns.length ] = ptnArg;
    }
  };
  sortArg(patterns, [], arg, arg.length);
  return patterns;
};

!function() {
  var i = 0,
      l = 0,
      allPatternsA,
      allPatternsB,
      randA,
      randB,
      ptnA,
      ptnB,
      isSame = true;

  adjustLength(menuA, menuB);
  console.log('▼ menuAの取り出し方の全パターン ▼');
  allPatternsA = generatePatterns(menuA);
  console.log('▼ menuBの取り出し方の全パターン ▼');
  allPatternsB = generatePatterns(menuB);

  randA = Math.floor( Math.random() * allPatternsA.length );
  ptnA = allPatternsA[randA];
  while(isSame) {
    randB = Math.floor( Math.random() * allPatternsB.length );
    ptnB = allPatternsB[randB];
    isSame = false;
    for(i=0, l=ptnA.length; i<l; i+=1) {
      // 取り出し順の値が被らないかチェック
      if(ptnA[i] === ptnB[i]) {
        isSame = true;
        break;
      }
    }
  }
  console.log('▼ menuA, menuB それぞれの取り出し順 ▼');
  console.log(ptnA, ptnB);

  console.log('▼ 決まった取り出し順に基いて順番に取り出す ▼');
  for(i=0, l=ptnA.length; i<l; i+=1) {
    var m1 = ptnA[i],
        m2 = ptnB[i];
    console.log(m1 + 'と' + m2);
  }
}();


追記
下記の手順で取り出す方法を追記します。
  1.  menu1をシャッフルするして順番を入れ替える
  2.  menu2をシャッフルする
  3.  インデックス順に各配列の要素を取り出してチェック。1回でも値が同じ場合があると2をやり直し
  4.  インデックス順に各配列の要素を取り出す

var menu1 = ["ジュース","チョコ","アメ"];
var menu2 = ["ジュース","チョコ","アメ"];
var sameFlg = true;

// fisher-yates アルゴリズム
// katoyさんの書かれている配列をシャッフルする方法
var shuffle = function(arg) {
  var i, j, l, tmp;
  for(l=arg.length, i=l-1; i>0; i-=1) {
    j = Math.floor(Math.random() * (i+1));
    tmp = arg[i];
    arg[i] = arg[j];
    arg[j] = tmp;
  }
  return arg;
};

// menu1をシャッフル
menu1 = shuffle(menu1);

while(sameFlg) {
  // menu2をシャッフル
  menu2 = shuffle(menu2);
  // 順番に取り出して同じ値が出てくる時はmenu2のシャッフルをやり直す
  sameFlg = false;
  for(var i = 0, l = menu1.length; i<l; i+=1) {
    if(menu1[i] === menu2[i]) {
      sameFlg = true;
      break;
    }
  }
}

console.log('▼ menu1, menu2 それぞれの取り出し順 ▼');
console.log(menu1, menu2);
for(var i = 0, l = menu1.length; i<l; i+=1) {
  console.log(menu1[i] + " と " + menu2[i]);
}


いずれにせよ
A. menu1のジュースとmenu2のジュースを区別する
  = それぞれの袋の中から1つづつ取り出しす かつ 取り出されたモノ(の種類)が被らないようにする
B. ジュースというも区別できないモノのが2つ有るとする
  = 大きな袋に全部入れてランダムに2つ取り出して、取り出されたモノが被らないようにする

のどちらかによってコードは変わってくると思います。
私が書いたのはいずれも、A. の考え方の場合です。

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2015/08/23 21:16

    各配列の取り出し方のバリデーションを全部列挙するより
    1. 配列Aをシャッフルするして順番を入れ替える
    2. 配列Bをシャッフルする
    3. インデックス順に各配列の要素を取り出してチェック。1回でも値が同じ場合があると2をやり直し
    4. インデックス順に各配列の要素を取り出す
    が一番コード数少なくて済みそう。

    キャンセル

+1

この問題は3つの要素を持つ配列から無作為に2つの要素を得るのと同じだと思います。
組み合わせは3通りなので同じ組み合わせを許さない要件であれば組み合わせを求めるだけで完了しますが、要件上は同じ組み合わせを複数回出力する事も可能なようです(["ジュース", "アメ"] を3回出力しても良い)。
方法としては3つ考えられますが、いずれも用意する配列は一つで済みます。

(方法1) 配列をシャッフルし、index が 01 の要素を取り出す処理を3回繰り返す
(方法2) 元の配列から2つの要素を取り出す組み合わせとなる配列を生成し、組み合わせ配列から無作為に一つ選ぶ処理を3回繰り返す
(方法3) 前方法で求めた組み合わせ配列1から3回無作為に選ぶ組み合わせ配列2を用意し、組み合わせ配列をシャッフルして index が 0 の要素を出力する

ここでは (方法2) を使ってコードを書きます。

function createPairPatternAll (array) {
  var pairPatterns = [];

  for (var i = 0, l = array.length, element; i < l; ++i) {
    for (var j = i + 1, element = array[i]; j < l; ++j) {
      pairPatterns.push([element, array[j]]);
    }
  }

  return pairPatterns;
}

function getPairs (array, size) {
  var pairPatterns = createPairPatternAll(array),
      l = pairPatterns.length,
      pairs = [];

  if (arguments.length < 2) {
    size = 1;
  }

  while (size--) {
    pairs.push(pairPatterns[Math.floor(Math.random() * l)]);
  }

  return pairs;
}

console.log(getPairs(['ジュース', 'チョコ', 'アメ'], 3)); // [['チョコ', 'アメ'], ['チョコ', 'アメ'], ['ジュース', 'アメ']]

console.log() は Chrome Developer Tools を使えば確認できます。
Google Chrome 以外のブラウザにも同様の機能があります。

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

0

何かしらの方法で重複チェックが必要ですね。

例えば、連想配列使って使用済みかチェックするとか...
  var menu1 = ["ジュース","チョコ","アメ"];
  var menu2 = ["ジュース","チョコ","アメ"];
  var used = [];

  for(var i = 0; i < 3;){
    //menuのrandom
    var rnd1 = Math.floor(Math.random() * menu1.length);
    var rnd2 = Math.floor(Math.random() * menu2.length);
    
    if (rnd1 == rnd2) {
        continue;
    }

    var key  = ((1 << rnd1) | (1 << rnd2));
    if (used[key] == true) {
        continue;
    }
    
    console.log(menu1[rnd1] + "と" + menu2[rnd2]);
    used[key] = true;
    i++;
  }

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2015/08/23 15:45

    質問の要件が定かで無い部分があるのですが、
    menu1のジュースが2本存在することになってしまう場合と言いますか、
    各配列から同じ値が複数回出力されてしまう可能性があるように思います。

    キャンセル

0

使った物を覚えておくしかないですね。
var menu1 = ["ジュース","チョコ","アメ"];
var menu2 = ["ジュース","チョコ","アメ"];
var shown = {}

var i = 0;
while( i < 3 ){
  //menuのrandom
  var rnd1 = Math.floor(Math.random() * menu1.length);
  var rnd2 = Math.floor(Math.random() * menu2.length);
  var msg = menu1[rnd1] + "と" + menu2[rnd2];
  if( shown[msg] ) continue;
  i++;
  shown[msg] = true;
  console.log(msg);
}

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2015/08/23 15:46

    コピペでコメントしてしまってスミマセンm_ _m
    こちらのコードも質問の要件が定かで無い部分があるのですが、
    menu1のジュースが2本存在することになってしまう場合と言いますか、
    各配列から同じ値が複数回出力されてしまう可能性があるように思います。

    キャンセル

0

質問文が求めている生成するメニューの制約制限がよくわかりません。
とりあえず、 menu1, menu2  から1つずつ選んで、すべての品物を3つに分配するという制限で
書いてみました。

ここでは、
menu1, memu2 をそれぞれ シャッフルして、 
   (menu1[0], menu2[0]) 
   (menu1[1], menu2[1]) 
   (menu1[2], menu2[2]) 
として、3つに分配しました。

// See http://garafu.blogspot.jp/2015/02/javascript_15.html
// 配列をシャッフルする。
function shuffle(arr) {
    var i, j, tmp, length;
    for (length = arr.length, i = length - 1; i > 0; i--) {
    j = Math.floor(Math.random() * (i + 1));
    tmp = arr[i];
    arr[i] = arr[j];
    arr[j] = tmp;
    }
    return arr;
}

var menu1 = ["ジュース","チョコ","アメ"];
var menu2 = ["ジュース","チョコ","アメ"];

menu1 = shuffle(menu1);
menu2 = shuffle(menu2);
for (var i = 0; i < 3; i++) {
    console.log(menu1[i] + " と " + menu2[i]);
}

配列のシャッフルについては、teratail でも過去に質問が出ています。
重複しない乱数

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

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

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

関連した質問

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

  • JavaScript

    17022questions

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