LoginSignup
5
5

More than 5 years have passed since last update.

JSON のストリームを Ruby で reduce するコマンド書いた

Last updated at Posted at 2014-10-02

2014-10-03 03:30 追記
jr として gem にして公開しました。


最近はログをとりあえず行指向の JSON に保存している、という方も多いと思います。
そしてそれを jq でフィルタしたりしている方も多いことでしょう。

問題はその結果に対して、何らかの集計処理を行いたいときです。
jq も reduce サポートしているようなのですが、どうやって書いていいかわからない。
というか、Ruby で書きたい。

そう思って、以下のようなコマンドを作ってみました。
仮に reduce-json と名づけます。
なお、この Ruby スクリプトのライセンスは The MIT License とします。

#!/usr/bin/env ruby
require 'yajl'

inputs = ARGV[2] ? ARGV[2..-1].map {|f| open f } : [STDIN]

json_enumerator = Enumerator.new do |yielder|
  inputs.each do |input|
    Yajl::load(input) do |d|
      yielder.yield d
    end
  end
end

puts Yajl::Encoder.encode(eval "json_enumerator.reduce(#{ARGV[0]}) #{ARGV[1]}")

Yajl が Enumerable を返してくれなくてちょっと困ったのですが、あっさり解決できました。
Ruby 凄い。

使い方

まずは上記の Ruby スクリプトを reduce-json として保存して、chmod +x します。

以下のようなアクセスログがあるとして、そこから各ユーザのアクセス数を集計してみましょう。
これを access_log.json として保存したとします。

{"user_id":1,"path":"/"}
{"user_id":2,"path":"/home"}
{"user_id":3,"path":"/my"}
{"user_id":1,"path":"/articles"}
{"user_id":1,"path":"/home"}
{"user_id":3,"path":"/"}
{"user_id":2,"path":"/articles/1"}
{"user_id":4,"path":"/my"}
{"user_id":1,"path":"/"}
{"user_id":2,"path":"/"}

そして以下のコマンドを実行します。
コマンドの第一引数が Enumerable#reduce の第一引数 (つまり初期値) で、第二引数はコードブロックです。
入力は第三引数にファイル名を渡すこともできますし、パイプから標準入力を受け取ることもできます。

$ ./reduce-json '{}' '{|acc, log| user_id = log["user_id"]; acc[user_id] = 0 unless acc[user_id]; acc[user_id] += 1; acc }' access_log.json
{"1":4,"2":3,"3":2,"4":1}

各ユーザごとのアクセス数が JSON として取得できました。
結果は JSON なので、jq に渡すこともできます。

$ ./reduce-json '{}' '{|acc, log| user_id = log["user_id"]; acc[user_id] = 0 unless acc[user_id]; acc[user_id] += 1; acc }' access_log.json | jq '.'
{
  "1": 4,
  "2": 3,
  "3": 2,
  "4": 1
}

簡単なスクリプトですが、UNIX 哲学にもとづいたクールなツールっぽいですね。

配布について

せっかくなので週末にでも Gem として配布できる状態にしておこうと思います。
それまでに、こういう実装がいいんじゃないかとか、こういうオプションが必要なんじゃないかとかあったらコメントください。
あと名前とか。

5
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
5