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

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

新規登録して質問してみよう
ただいま回答率
85.35%
JavaScript

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

Q&A

解決済

6回答

11091閲覧

二次元配列内の数値を加算してまとめたい

kado_

総合スコア35

JavaScript

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

0グッド

3クリップ

投稿2015/11/30 12:25

[["a",10], ["b",20], ["c",30], ["a",40], ["a",50]]

このような配列の
1つめの要素が一致する場合
2つめの要素の数値を合算した配列を作りたいのです。
(この場合はaの数値をまとめたい)

▼期待する形

[["a",100], ["b",20], ["c",30]]

こういった場合、みなさんがどのように実装しているのか
ご教授いただければ幸いです。

よろしくお願いします。

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答6

0

最近はRamdaにはまっています。

JavaScript

1var R = require("ramda"); 2var data = [["a",10],["b",20],["c",30],["a",40],["a",50]]; 3var result = R.toPairs(R.map(R.pipe(R.map(R.last), R.sum), R.groupBy(R.head, data))); 4console.log(result); // => [ [ 'a', 100 ], [ 'b', 20 ], [ 'c', 30 ] ]

関数型プログラミングは難しいです。もうちょっとすっきり書ける方法があるような気がします。

さらに関数型プログラミングっぽくしてみました。関数にしたから、再利用も可能です。

JavaScript

1var R = require("ramda"); 2var data = [["a",10],["b",20],["c",30],["a",40],["a",50]]; 3var mapSum = R.pipe(R.groupBy(R.head), R.map(R.pipe(R.map(R.last), R.sum)), R.toPairs); 4console.log(mapSum(data)); // => [ [ 'a', 100 ], [ 'b', 20 ], [ 'c', 30 ] ]

投稿2015/11/30 14:57

編集2015/12/01 10:31
raccy

総合スコア21739

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kado_

2015/12/01 05:41

Ramdaはまだ使ったことがありませんでした。 こんなに短くて済むのですね!
guest

0

ベストアンサー

この状況で躓いてるようでしたら、Array#map 等の関数を使わず、for 文でコードを書いてみる事をお勧めします。
まず、頭の中でアルゴリズムを組み立てられるようになる事が重要ですが、その場合は for 文で地道に組んだほうが頭に入りやすいと思います。
Array#map を使った手法は for 文の手法を習得してから覚えても遅くはありません。

JavaScript

1'use strict'; 2/** 3 * Array.prototype.indexOf 版 4 */ 5function sample_indexOf (array) { 6 var keys = [], 7 values = [], 8 results = []; 9 10 for (var i = 0, l = array.length, element, index; i < l; ++i) { 11 element = array[i]; 12 index = keys.indexOf(element[0]); 13 14 if (index !== -1) { 15 values[index] += element[1]; 16 } else { 17 keys.push(element[0]); 18 values.push(element[1]); 19 } 20 } 21 22 for (var i = 0, l = keys.length; i < l; ++i) { 23 results.push([keys[i], values[i]]); 24 } 25 26 return results; 27} 28 29/** 30 * ObjectLiteral 版 31 */ 32function sample_ObjectLiteral (array) { 33 var map = {}, 34 results = []; 35 36 for (var i = 0, l = array.length, element, key; i < l; ++i) { 37 element = array[i]; 38 key = element[0]; 39 40 if (map.hasOwnProperty(key)) { 41 map[key] += element[1]; 42 } else { 43 map[key] = element[1]; 44 } 45 } 46 47 for (var keys = Object.keys(map), i = 0, l = keys.length; i < l; ++i) { 48 results.push([keys[i], map[keys[i]]]); 49 } 50 51 return results; 52} 53 54/** 55 * Map 版 (ES6) 56 * @url https://github.com/paulmillr/es6-shim 57 */ 58function sample_map (array) { 59 var map = new Map, 60 results = []; 61 62 for (var i = 0, l = array.length, element, key; i < l; ++i) { 63 element = array[i]; 64 key = element[0]; 65 66 map.set(key, map.has(key) ? map.get(key) + element[1] : element[1]); 67 } 68 69 for (var iterator = map.entries(), i = 0, l = map.size; i < l; ++i) { 70 results.push(iterator.next().value); 71 } 72 73 return results; 74} 75 76/** 77 * Array.prototype.forEach + Array.prototype.find 版 (ES6) 78 */ 79 var sample_forEach = (function (findfn) { 80 function eachfn (element) { 81 var existingElement = this.find(findfn, element[0]); 82 83 if (typeof existingElement !== 'undefined') { 84 existingElement[1] += element[1]; 85 } else { 86 this.push(element); 87 } 88 } 89 90 return function sample_forEach (array) { 91 var results = []; 92 93 array.forEach(eachfn, results); 94 return results; 95 }; 96}(function findfn (element) { 97 return element[0] === this; 98})); 99 100/** 101 * Array.prototype.reduce + + Array.prototype.find 版 (ES6) 102 */ 103var sample_reduce = (function (findfn) { 104 function reducefn (previous, current){ 105 var existingElement = previous.find(findfn, current[0]); 106 107 if (typeof existingElement !== 'undefined') { 108 existingElement[1] += current[1]; 109 } else { 110 previous.push(current); 111 } 112 113 return previous; 114 } 115 116 return function sample_reduce (array) { 117 return array.reduce(reducefn, []); 118 } 119}(function findfn (element) { 120 return element[0] === this; 121})); 122 123var array = [['a',10], ['b',20], ['c',30], ['a',40], ['a',50]]; 124 125console.log(JSON.stringify(sample_indexOf(array))); // [["a",100],["b",20],["c",30]] 126console.log(JSON.stringify(sample_ObjectLiteral(array))); // [["a",100],["b",20],["c",30]] 127console.log(JSON.stringify(sample_map(array))); // [["a",100],["b",20],["c",30]] 128console.log(JSON.stringify(sample_forEach(array))); // [["a",100],["b",20],["c",30]] 129console.log(JSON.stringify(sample_reduce(array))); // [["a",100],["b",20],["c",30]]

個人的には ES6 の Mapmap オブジェクトを返す仕様に変更する事がお勧めです。
が、new Map は "es6-shim" 等の polyfill を使わないとクロスブラウザが出来なく、サンプルコードも少ない為、今すぐに習得しなくても良いと思います。
実際のところは ObjectLiteral で上手くいくケースが大半です。


(2015/12/04 23:07追記)

Array#forEach, Array#reduce, Array#find を使ったコードを追記しました。

Re: kado_ さん

投稿2015/12/01 05:16

編集2015/12/04 14:07
think49

総合スコア18189

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kado_

2015/12/01 05:53

レベルに合わせて、丁寧にありがとうございました。おっしゃるとおり、まずはforをしっかり理解しようと思います。今はforが二重になると頭が混乱します。。
think49

2015/12/04 14:20

Array#forEach, Array#reduce, Array#find を使ったコードを追記しました。
guest

0

ここでLiveScriptを使って参戦!

[["a",10],["b",20],["c",30],["a",40],["a",50]] |> group-by (.0) |> Obj.map -> it |> map (.1) |> fold1 (+) |> obj-to-pairs # [["a",100],["b",20],["c",30]]

わぁい、たったの4行!
http://livescript.net/ ←右側のミニコンソールでお試しあれ

投稿2015/12/03 15:05

編集2015/12/03 23:24
miyabi-sun

総合スコア21203

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

miyabi-sun

2015/12/03 15:35

think49さんのツッコミを回避できないのと、 配列→オブジェクト→配列と中々メモリ虐めがひどいので、 チューニングするならreduceでも使った方がいいですかね?
raccy

2015/12/03 21:55

関数型にチューニングなどいらぬ!! あらゆるオブジェクトをimmutableとして扱う関数型プログラミングにおいて、オブジェクト生成によるメモリ消費を防ぐには、まともな遅延評価が実装されないと厳しいんじゃないでしょうかねー。LiveScriptもそうですけど、純粋関数型のPureScriptですら遅延評価にできなかった事を考えると、JavaScriptで関数型プログラミングってある程度パフォーマンス度外視にしないと難しいと思っています。
miyabi-sun

2015/12/03 23:31 編集

ナルホド!ありがとうございます。 確かに「JavaScript 遅延評価」等でそれらしい記事は出てきますけど、 全ての問題が解決しそうな雰囲気でもないですね… そういう事をしたいなら言語自体を変えるとか…暫くは使い分けが大事のようですね。
think49

2015/12/04 14:23

呼ばれた気がしたので来ました。 [Compile] したコードは28行ですが、それだけでは動かなくて http://gkz.github.io/prelude-ls/prelude-browser.js にある膨大なコードを取り込まなくては動きません。 最小限の拡張コードはどうなるかと解析してみましたが、拡張が必要なグローバル関数が多すぎて諦めました。 ECMAScripter としては拡張コードを含めたコードで判断するので「コード量的にもアルゴリズム的にも無駄が多いのではないかな」というのが正直な感想です。 実際に私が書くとしたらアルゴリズムの効率重視で書くと思いますが、こういうコードを書く人はコーディングを楽しむ為にパフォーマンスを犠牲にしたり、コーディング時間を減らす事を優先する傾向があるので、各々の優先順位の意識の違いが現れているだけでどちらが良いといえるものでもないかなと。 ただ、こういうコードは初心者にお勧めするものではないとは思います。 読みにくい事もさることながら拡張コードが多い程にデバッグが複雑になって大変な事になります。 Array#reduce はいいですね。ふと思いついたので、コードを書き出してみました。 https://teratail.com/questions/21402#r33781
guest

0

まず coffeescript で書いてみました。次にそれを coffee -pb で javascript にしてみました。

coffeescript

1data = [["a",10], 2["b",20], 3["c",30], 4["a",40], 5["a",50]]; 6 7h = {} 8for d in data 9 key = d[0] 10 if h[key] 11 h[key] += d[1] 12 else 13 h[key] = d[1] 14 15ans = [] 16for key, val of h 17 ans.push [key, val] 18 19console.log ans

実行結果

coffee 1.coffee [ [ 'a', 100 ], [ 'b', 20 ], [ 'c', 30 ] ]
$ coffee -pb 1.coffee > 1.js $ node 1.js [ [ 'a', 100 ], [ 'b', 20 ], [ 'c', 30 ] ] $ cat 1.js var ans, d, data, h, i, key, len, val; data = [["a", 10], ["b", 20], ["c", 30], ["a", 40], ["a", 50]]; h = {}; for (i = 0, len = data.length; i < len; i++) { d = data[i]; key = d[0]; if (h[key]) { h[key] += d[1]; } else { h[key] = d[1]; } } ans = []; for (key in h) { val = h[key]; ans.push([key, val]); } console.log(ans);

投稿2015/11/30 13:22

編集2015/11/30 19:13
katoy

総合スコア22324

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kado_

2015/12/01 05:40

回答の早さに感激です。ありがとうございます!
think49

2015/12/01 05:43

意地悪な指摘ですが、data = [["hasOwnProperty", 1]]; 又は Object.prototype.a = 1; で破綻しますね…。 前者は Object#hasOwnProperty 又は Object.create(null) で、後者は Object.keys 又は Object#hasOwnProperty で回避可能です。
guest

0

普通な感じで。

javascript

1(function (arr){ 2 return arr.map(function(elm){ 3 return elm[0]; 4 }) 5 .filter(function(elm, i, self){ 6 return i == self.indexOf(elm); 7 }) 8 .map(function(key){ 9 return [key, arr.reduce(function(sum, elm){ 10 return sum += elm[0] == key ? elm[1] : 0; 11 },0)]; 12 }) 13})( 14 [["a",10], 15 ["b",20], 16 ["c",30], 17 ["a",40], 18 ["a",50]] 19);

ついでにfirefoxじゃないと動かない書き方で。

javascript

1(function(arr){ 2 return [ 3 [x, arr.reduce(function(sum, [k, v]){ 4 return sum += k==x ? v : 0; 5 }, 0)] 6 for 7 (x of new Set( 8 [ k for each ([k, ] in arr ) ] 9 )) 10 ] 11})([ 12 ["a",10], 13 ["b",20], 14 ["c",30], 15 ["a",40], 16 ["a",50] 17]);

投稿2015/12/01 01:08

編集2015/12/01 01:52
Lhankor_Mhy

総合スコア36963

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kado_

2015/12/01 05:49

filterとmapは使ったことがなかったです。とても便利ですね!
guest

0

対象要素Aと同じワードを持つ要素Bを検索し、見つけたらBの値をAに加算しBを破壊していくババ抜き的方法。

JavaScript

1var data = [["a",10], ["b",20], ["c",30], ["a",40], ["a",50]]; 2 3for(var i = 0; i < data.length; i++){ 4 for(var j = i+1; j < data.length; j++){ 5 6 if(data[i][0] == data[j][0]){ 7 8 data[i][1] += data[j][1]; 9 data.splice(j--, 1); 10 } 11 } 12} 13 14console.log(data);

spliceの負荷が気になる。

投稿2015/11/30 15:23

nage

総合スコア144

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kado_

2015/12/01 05:42

これはわかりやすい! spliceは負荷が高いのですか?気にせず使っていました・・
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問