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

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

ただいまの
回答率

90.40%

  • JavaScript

    18136questions

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

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

解決済

回答 6

投稿

  • 評価
  • クリップ 3
  • VIEW 2,352

kado_

score 27

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

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

▼期待する形
[["a",100],
["b",20],
["c",30]]

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

よろしくお願いします。
  • 気になる質問をクリップする

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 6

+4

最近はRamdaにはまっています。
var R = require("ramda");
var data = [["a",10],["b",20],["c",30],["a",40],["a",50]];
var result = R.toPairs(R.map(R.pipe(R.map(R.last), R.sum), R.groupBy(R.head, data)));
console.log(result); // => [ [ 'a', 100 ], [ 'b', 20 ], [ 'c', 30 ] ]
関数型プログラミングは難しいです。もうちょっとすっきり書ける方法があるような気がします。

さらに関数型プログラミングっぽくしてみました。関数にしたから、再利用も可能です。
var R = require("ramda");
var data = [["a",10],["b",20],["c",30],["a",40],["a",50]];
var mapSum = R.pipe(R.groupBy(R.head), R.map(R.pipe(R.map(R.last), R.sum)), R.toPairs);
console.log(mapSum(data)); // => [ [ 'a', 100 ], [ 'b', 20 ], [ 'c', 30 ] ]

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2015/12/01 14:41

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

    キャンセル

checkベストアンサー

+3

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

'use strict';
/**
 * Array.prototype.indexOf 版
 */
function sample_indexOf (array) {
  var keys = [],
      values = [],
      results = [];

  for (var i = 0, l = array.length, element, index; i < l; ++i) {
    element = array[i];
    index = keys.indexOf(element[0]);

    if (index !== -1) {
      values[index] += element[1];
    } else {
      keys.push(element[0]);
      values.push(element[1]);
    }
  }

  for (var i = 0, l = keys.length; i < l; ++i) {
    results.push([keys[i], values[i]]);
  }

  return results;
}

/**
 * ObjectLiteral 版
 */
function sample_ObjectLiteral (array) {
  var map = {},
      results = [];

  for (var i = 0, l = array.length, element, key; i < l; ++i) {
    element = array[i];
    key = element[0];

    if (map.hasOwnProperty(key)) {
      map[key] += element[1];
    } else {
      map[key] = element[1];
    }
  }

  for (var keys = Object.keys(map), i = 0, l = keys.length; i < l; ++i) {
    results.push([keys[i], map[keys[i]]]);
  }

  return results;
}

/**
 * Map 版 (ES6)
 * @url https://github.com/paulmillr/es6-shim
 */
function sample_map (array) {
  var map = new Map,
      results = [];

  for (var i = 0, l = array.length, element, key; i < l; ++i) {
    element = array[i];
    key = element[0];

    map.set(key, map.has(key) ? map.get(key) + element[1] : element[1]);
  }

  for (var iterator = map.entries(), i = 0, l = map.size; i < l; ++i) {
    results.push(iterator.next().value);
  }

  return results;
}

/**
 * Array.prototype.forEach + Array.prototype.find 版 (ES6)
 */
 var sample_forEach = (function (findfn) {
  function eachfn (element) {
    var existingElement = this.find(findfn, element[0]);

    if (typeof existingElement !== 'undefined') {
      existingElement[1] += element[1];
    } else {
     this.push(element);
    }
  }

  return function sample_forEach (array) {
    var results = [];

    array.forEach(eachfn, results);
    return results;
  };
}(function findfn (element) {
  return element[0] === this;
}));

/**
 * Array.prototype.reduce + + Array.prototype.find 版 (ES6)
 */
var sample_reduce = (function (findfn) {
  function reducefn (previous, current){
    var existingElement = previous.find(findfn, current[0]);

    if (typeof existingElement !== 'undefined') {
      existingElement[1] += current[1];
    } else {
      previous.push(current);
    }

    return previous;
  }

  return function sample_reduce (array) {
    return array.reduce(reducefn, []);
  }
}(function findfn (element) {
  return element[0] === this;
}));

var array = [['a',10], ['b',20], ['c',30], ['a',40], ['a',50]];

console.log(JSON.stringify(sample_indexOf(array)));  // [["a",100],["b",20],["c",30]]
console.log(JSON.stringify(sample_ObjectLiteral(array)));  // [["a",100],["b",20],["c",30]]
console.log(JSON.stringify(sample_map(array)));  // [["a",100],["b",20],["c",30]]
console.log(JSON.stringify(sample_forEach(array)));  // [["a",100],["b",20],["c",30]]
console.log(JSON.stringify(sample_reduce(array)));  // [["a",100],["b",20],["c",30]]

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


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

Array#forEachArray#reduceArray#find を使ったコードを追記しました。

Re: kado_ さん

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2015/12/01 14:53

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

    キャンセル

  • 2015/12/04 23:20

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

    キャンセル

+2

ここで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/04 00:35

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

    キャンセル

  • 2015/12/04 06:55

    関数型にチューニングなどいらぬ!!

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

    キャンセル

  • 2015/12/04 08:23 編集

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

    キャンセル

  • 2015/12/04 23:23

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

    Array#reduce はいいですね。ふと思いついたので、コードを書き出してみました。
    https://teratail.com/questions/21402#r33781

    キャンセル

+1

まず coffeescript で書いてみました。次にそれを coffee -pb で javascript にしてみました。
data = [["a",10],
["b",20],
["c",30],
["a",40],
["a",50]];

h = {}
for d in data
  key = d[0]
  if h[key]
    h[key] += d[1]
  else
    h[key] = d[1]

ans = []
for key, val of h
  ans.push [key, val]

console.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/12/01 14:40

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

    キャンセル

  • 2015/12/01 14:43

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

    キャンセル

0

対象要素Aと同じワードを持つ要素Bを検索し、見つけたらBの値をAに加算しBを破壊していくババ抜き的方法。
var data = [["a",10], ["b",20], ["c",30], ["a",40], ["a",50]];

for(var i = 0; i < data.length; i++){
    for(var j = i+1; j < data.length; j++){
        
        if(data[i][0] == data[j][0]){
            
            data[i][1] += data[j][1];
            data.splice(j--, 1);
        }
    }
}

console.log(data);
spliceの負荷が気になる。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2015/12/01 14:42

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

    キャンセル

0

普通な感じで。
(function (arr){
  return arr.map(function(elm){
    return elm[0];
  })
  .filter(function(elm, i, self){
    return i == self.indexOf(elm);
  })
  .map(function(key){
    return [key, arr.reduce(function(sum, elm){
      return sum += elm[0] == key ? elm[1] : 0; 
    },0)];
  })
})(
  [["a",10],
   ["b",20],
   ["c",30],
   ["a",40],
   ["a",50]]
);



ついでにfirefoxじゃないと動かない書き方で。
(function(arr){
  return [
    [x, arr.reduce(function(sum, [k, v]){
      return sum += k==x ? v : 0;
    }, 0)]
    for
    (x of new Set(
      [ k for each ([k, ] in arr ) ]
    ))
  ]
})([
  ["a",10],
  ["b",20],
  ["c",30],
  ["a",40],
  ["a",50]
]);

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2015/12/01 14:49

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

    キャンセル

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

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

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

  • JavaScript

    18136questions

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