http://q.hatena.ne.jp/1448507045
このような連想配列でキーごとに4番目のスコア(rec)の部分の平均値を求めてキーごとに表示させたいのですが方法がわかりません。
例)
UNIT1の平均値は@@点です。
UNIT2の平均値は@@点です。
連想配列に入れた後で、キーごとにスコアの数を数えて、その回数で割る方法を考えましたが合計点を求めるところがわかりません。また連想配列のデータのスコアの部分の合計点を求める場合、やはり、連想配列に入れたあとで、処理するのでしょうか?あるいは、入れながら足していくのか? 調べてみても1つのキーに対して数が1つの例しか見つからず、上のように1つのキーに得点が複数ある場合の処理の方法が理解できずにいます。アドバイスいたたければ幸いです。
var tableMap = { UNIT1:["80", "40"], UNIT2:["50", "60", "90"] }; var averageMap = Object.keys(tableMap).reduce(function(map, key){ var row = tableMap[key]; map[key] = row.reduce(function(sum, value){return sum += parseFloat(value)},0) / row.length return map; },{}); //{UNIT1:60, UNIT2:66.66666666666667}
無理に副作用を嫌ってかえって分かりにくいコードになる図。
分かりやすい回答はa-kuma3さんお願いします。
ベタに書くとこんな感じになります。
var tableMap = { UNIT1:["80", "40"], UNIT2:["50", "60", "90"] }; var averageMap = {}; for (var key in tableMap) { var data = tableMap[key]; var sum = 0; for (var i = 0 ; i < data.length ; ++i) { sum += parseFloat(data[i]); } averageMap[key] = sum / data.length; } console.log(averageMap); // Object { UNIT1: 60, UNIT2: 66.66666666666667 }
やってることは id:Lhankor_Mhy さんが書いたのと同じです。
Array#reduce って、使いどころが難しいメソッドだと思います。
メソッド名を素直に受け取れば、Array#filter メソッドで代替できるし、合計を求めるような N → 1 の「削減する」だったら「集約する」とかの方がメソッド名としてはピンとくるような感じがします。
ランカーさんの回答例にならったコードを回答には書きましたが、前の質問であったようなケースに組み込むのであれば、こんな感じかと。
https://jsfiddle.net/a_kuma3/yecrp2c1/1/
点数の表にくっつけるなら、こんな感じです。
https://jsfiddle.net/a_kuma3/k15k6k2w/2/
# 平均を求める所よりも、見た目を作るところの方が面倒です :-)
未実施を処理するループのところなんですが、i = 2 の三回目のループのときに、
averagedata[i] つまり、averagedata[2] が未定義なので、averagedata[i][0] を参照しようとしてエラーになってます(そこで、ループが止まる)。
ここでやりたいのは、averagedata に listdata[i] が、同じ順番のところではなく、どこかにあるかどうかを探さなくちゃいけません。
元になる得点データ dataArray4 を UNIT1 と UNIT2 ではなく、UNIT3 と UNIT4 に変えて試してみれば分かると思います。
こうしてみると、考えやすいと思います。
function searchAverageData(data, name) { // 配列の配列 data から、data[i][0] == name を探す ... return 平均得点; // もし、見つからなかったら -1 を返す } result1 =[]; result =[]; for (var i = 0 ; i < listdata.length ; ++i) { var average = searchAverageData(averagedata, listdata[i]); if (average != -1){ // 見つからなかったら -1 が返る alert("○:リストにありました " + i + " : " + listdata[i]) result1.push(averagedata[i][0]) //keyを入れる result1.push(averagedata[i][1]) //平均値を入れる result.push(result1) } else{ alert("×:リストにありません " + i + " : " + listdata[i]) result1.push(averagedata[i][0]) //keyを入れる result1.push("未実施") //未実施を入れる result.push(result1) } alert(result[i]) }
# ループの部分は、if の判定のところしか変えてません
「averagedata から listdata のそれぞれの名前を探す」というところが解決できれば、
ということに、気付くと思います。
a-kuma3 さん、何度も恐縮です。
面倒になったら応えなくなるだけなので、あまり気にしなくて良いですよ。
そういうサイトなわけだし :-)
a-kuma3さん、ありがとうございます。
https://jsfiddle.net/kajironpu/vqr6vq50/2/
なるほど、理解できました。
for (var i = 0 ; i < listdata.length ; ++i) {
if (averagedata[i][0] == listdata[i]){
ここで、リストの数だけ順番に回して探そうとしたのですが、averageデータのほうは実際は
少ない場合はデータがないのでエラーになっていたんですね。
firefoxでundefinedというのはそういうことだったのですね
function searchAverageData(data, name) {
// 配列の配列 data から、data[i][0] == name を探す
...
return 平均得点; // もし、見つからなかったら -1 を返す
}
上の関数で、データを見つけて取ってくるやり方(引数というやつでしょうか)
を是非トライしてみます。
ありがとうございます。
Lhankor_Mhyさん
2015/12/14 10:25:00さっそくありがとうございます。
お返事遅れて申し訳ありませんでした。
なかなかLhankor_Mhyさんのコードを理解するのは難しいですがとてもシンプルにまとまるのですね
じっくり勉強します。
ありがとうございました。