# add method
# 1) include_childs
# 2) ext_hashfy
# 3) only_hashfy
# 4) to_ext_json
# 5) add_top_key
# 6) search_val
# 7) dist
# 8) extract
class Array
  #== 子テーブルのデータ追加
  # ハッシュ配列（親）に、ハッシュ配列（子）を追加する。
  #_child_cls_:: 子のActiveRecordクラス
  #
  # 戻り値:: self
  #-----------------------------------------------------------------#++
  def include_childs(child_cls = nil)
    tbl_name = child_cls.to_s.tableize
    cnt_name = tbl_name + '_count'
    # 子を持つ親IDを編集
    parent_ids = []
    target_parents = []
    self.each do |hr|
      if hr[cnt_name].to_i > 0
        parent_ids.push(hr['id'])
        target_parents.push(hr)
      end
    end
    
    # 子検索
    parent_key = child_cls.new.parent_key
    childs = child_cls.passign(parent_key, parent_ids).all.ext_hashfy
    
    # 子データ追加
    target_parents.each do |parent|
      target_childs = childs.select {|r| (r[parent_key] == parent['id']) }
      parent[tbl_name] = target_childs
    end
    return self
  end

  #== ActiveRecord配列→ハッシュ配列変換メイン
  #_add_:: ハッシュ配列に、項目を追加するための情報
  #
  # 戻り値:: ハッシュ配列
  #-----------------------------------------------------------------#++
  def ext_hashfy(adds = {})
    adds[:method] ||= {}
    adds[:disp] ||= {}
    results = []
    if self.length > 0
      results = self.only_hashfy(adds[:method]).add_disp_names(adds[:disp])
    end
    return results
  end

  #== ActiveRecord配列→ハッシュ配列変換
  #_adds_:: ハッシュ配列に、項目を追加するための情報（ハッシュキーと値生成メソッド）
  #
  # 戻り値:: ハッシュ配列
  #-----------------------------------------------------------------#++
  def only_hashfy(adds = [])
    results = []
    self.each do |ar|
      results.push(ar.only_hashfy(adds))
    end
    return results
  end

  #== ActiveRecord配列→ハッシュ配列変換と同時にキーとindexのハッシュペアを作成する。
  #_adds_:: ハッシュ配列に、項目を追加するための情報（ハッシュキーと値生成メソッド）
  #
  # 戻り値:: ハッシュ配列
  #-----------------------------------------------------------------#++
  def index_hashfy(adds = [], index_key='')
    results = []
    hash = Hash.new
    self.each_with_index do |ar, i|
      rec = ar.only_hashfy(adds)
      results.push(rec)
      hash.store(rec[index_key], i) unless rec[index_key].blank?
    end
    return results, hash
  end

  #== JSON編集
  #_count_:: ページング処理なしの全体のレコード数
  #_tab_name_:: テーブル名
  #
  # 戻り値:: JSONデータ
  #-----------------------------------------------------------------#++
    def to_ext_json(tab_name = nil, count = nil)
      unless count
        count = self.length
      end
      
      return {'results' => count, tab_name  => self}.to_json
    end

  #== ハッシュ配列にトップキーを追加
  # ex. {:a=>val, :b=val} ----> {:key=>{:a=>val, :b=val}}
  #_key_:: 追加キー
  #
  # 戻り値:: self
  #-----------------------------------------------------------------#++
  def add_top_key(top_key = "")
    self.map! {|item| {top_key => item}}
    return self
  end

  #== ActiveRecord、又はハッシュの配列検索
  # 指定したキー項目の値と、検索値が一致するインデックスを返却する。
  #_val_:: 検索値
  #_key_:: キー
  #
  # 戻り値:: 見つかった位置
  #-----------------------------------------------------------------#++
  def _search_val(val = {}, keys = [])
    result = nil
    self.each_with_index{|r, idx|
      all_match_flag = true
      keys.each{|key|
        if r.get_val(key) != val[key]
          all_match_flag = false
          break
        end
      }
      if all_match_flag
        result = idx
        break
      end
    }
    
    return result
  end
  def search_val(val = nil, key = nil)
    if key.instance_of?(Array)
      _key = key
      _val = val
    else
      _key = [key]
      _val = {key => val}
    end
    result = self._search_val(_val, _key)
    return result
  end

  #== ActiveRecord、又はハッシュの配列検索
  # 指定したキー項目の値と、検索値が一致するインデックスを返却する。
  #_val_:: 検索値
  #_key_:: キー
  #
  # 戻り値:: 見つかったActiveRecord、又はハッシュ
  #-----------------------------------------------------------------#++
  def get_by_search_val(val = nil, key = nil)
    idx = self.search_val(val, key)
    return self[idx]
  end

  #== ActiveRecord、又はハッシュの配列検索
  # 指定したキー項目の値と、検索値が一致する配列を返却する。
  #_val_:: 検索値
  #_key_:: キー
  #
  # 戻り値:: 見つかった位置
  #-----------------------------------------------------------------#++
  def select_by_search_val(val = nil, key = nil)
    results = []
    self.each do |r|
      check_val = r.get_val(key)
      if check_val == val
        results.push(r)
      end
    end
    return results
  end

  #== DISTINCT処理
  # 配列から、指定したキーが重複するレコードを取り除く。
  #_key_:: キー
  #
  # 戻り値:: 重複を取り除いたレコード配列
  #-----------------------------------------------------------------#++
  def _dist(keys = [])
    results = []
    self.each do |r|
      unless results._search_val(r, keys)
        results.push(r.deep_copy)
      end
    end
    return results
  end
  def dist(key = nil)
    _key = (key.instance_of?(Array)) ? key : [key]
    results = self._dist(_key)
    return results
  end

  #== 項目抽出処理
  # 配列から、指定したキー項目のみを抽出する。
  #_key_:: キー
  #
  # 戻り値:: 抽出した項目の配列
  #-----------------------------------------------------------------#++
  def extract(key = nil)
    results = self.map {|r| r.get_val(key) }
    return results
  end

#protected
#外から使えるメソッドにするために、protectedは外しました. 2011/1/11 by shimizu
  #== 表示名追加
  #_opts_:: 表示名と一緒に追加取得するカラム情報
  #
  # 戻り値:: self
  #-----------------------------------------------------------------#++
  def add_disp_names(opts = {})
    return self if self.blank?
    col_names = self.first.keys
    req_cols = []
    disp_keys = opts[:disp_keys] || {}
    disp_add_keys = opts[:disp_add_keys] || {}
    opts[:multiple_id_cols] ||= {}
    col_names.each do |col_name|
      # 除外カラム
      if opts[:except_cols] and opts[:except_cols].has_value?(col_name)
        next
      end
      # 追加取得カラム編集
      if opts[:req_cols]
        opts[:req_cols].each do |req_col|
          if req_col[:base] == col_name
            req_cols = req_col[:add]
            break
          end
        end
      end
      opts[:disp_key] = disp_keys[col_name] || 'disp_name'
      opts[:multiple_id_col] = opts[:multiple_id_cols][col_name] ? true : false
      # 表示名追加実行
      Comm::Tool::DispName.add_disp_name(self, col_name, (req_cols + (disp_add_keys[col_name]||[])), opts)
      req_cols.clear
    end
    return self
  end
  #正規分布の平均、分散、標準偏差を求めるメソッド
  # 要素をto_iした値の平均を算出する
  def avg
    size > 0 ? inject(0.0){|r,i| r+=i.to_i }/size : 0.0
  end
  # 要素をto_iした値の分散を算出する
  def variance(var_avg=nil)
    a = var_avg || avg
    size > 0 ? inject(0.0){|r,i| r+=(i.to_i-a)**2 }/size : 0.0
  end
  # 要素をto_iした値の標準偏差を算出する
  def standard_deviation(var_variance=nil, var_avg=nil)
    v = var_variance || variance(var_avg)
    Math.sqrt(v)
  end  
  def padding_item(total_size, item)
    if size < total_size
      until size >= total_size do
        self.push(item)
      end
    end
    return self
  end
end
