yipuran-core から CSV処理機能を他に分岐する

長い間、CSV読み書きする処理を、yipuran-core ライブラリで抱えこんでいたが、

  • yipuran-core に様々な処理が増えてきた。
  • CSV書込み処理を強化したい。

という理由で、yipuran-core のバージョン 4.19 ( Ver 4.19 ) より、

CSV読み書きの処理を、yipuran-csv というライブラリに移しました。

GitHub - yipuran/yipuran-csv

Home · yipuran/yipuran-csv Wiki · GitHub

yipuran-core のバージョン 4.18 は、まだ Git-hub の maven リポジトリに残ってはいるのでしばらく
リポジトリから 4.18を取得して使用できますが、4.19 が出たので、今までのCSV処理を使用しつつ、
他の機能を使うなら、 yipuran-core Ver 4.19以上と、yipuran-csv の両方使用するようにすべきでしょう。

配列のマップのキー自動チェック登録

リストのケースに触発されて、配列の場合、、、

import java.io.Serializable;
import java.lang.reflect.Array;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.UnaryOperator;
/**
 * Map<K, V[]> key存在自動チェック登録.
 */
@FunctionalInterface
public interface Maparrayadd<K, V> extends Serializable{

   void accept(K k, V v);

   public static <K, V> Maparrayadd<K, V> of(Map<K, V[]> m, int length){
      return (k, v)->{
         m.put(k, Optional.ofNullable(m.get(k))
                  .map(e->arrayAddReturn(v).apply(e))
                  .orElse(new Function<V, V[]>(){
                     @SuppressWarnings("unchecked")
                     @Override
                     public V[] apply(V u){
                        V[] ary = (V[])Array.newInstance(u.getClass(), length);
                        ary[0] = u;
                        return ary;
                     }
                  }.apply(v)));
      };
   }

   static <T> UnaryOperator<T[]> arrayAddReturn(T t){
      return a->{
         for(int i=0;i < a.length;i++){
            if (a[i]==null) {
               a[i] = t;
               break;
            }
         }
         return a;
      };
   }
}

配列の長さを超える add は無視されるようになっている。
よくある配列のインデックス例外を発生させない。

サンプル

Map<String, Integer[]> map = new HashMap<>();

Maparrayadd<String, Integer> m = Maparrayadd.of(map, 3);

m.accept("A", 1);
m.accept("A", 2);
m.accept("A", 3);
m.accept("A", 4);

m.accept("B", 10);
m.accept("B", 20);

System.out.println(map);

map.entrySet().stream().forEach(e->{
	System.out.println("key = "+ e.getKey());
	Arrays.stream(e.getValue()).forEach(System.out::println);
});

結果

{A=[Ljava.lang.Integer;@4dd8dc3, B=[Ljava.lang.Integer;@6d03e736}
key = A
1
2
3
key = B
10
20
null

Setコレクションのマップキーチェック add

リストMap の リストに要素を追加(3) - Oboe吹きプログラマの黙示録

リストMap の リストに要素を追加(4) - Oboe吹きプログラマの黙示録

を書いたので、Set の方も、、、

Set の場合は、TreeSet や、HashSet を指定できた方が良いので、未指定→HashSet として
この関数インターフェース生成のメソッドは2つ用意しました。

import java.io.Serializable;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.function.UnaryOperator;
/**
 * Map<K, Set<V>> key存在自動チェック Set add
 */
@FunctionalInterface
public interface MapSetpush<K, V> extends Serializable{

   void accept(K k, V v);

   public static <K, V> MapSetpush<K, V> of(Map<K, Set<V>> m){
      return (k, v)->{
         m.put(k, Optional.ofNullable(m.get(k))
                  .map(e->setAddReturn(v).apply(e))
                  .orElseGet(()->new HashSet<V>(Arrays.asList(v))));
      };
   }

   public static <K, V> MapSetpush<K, V> of(Map<K, Set<V>> m, Supplier<Set> newsupplier){
      return (k, v)->{
         m.put(k, Optional.ofNullable(m.get(k))
                  .map(e->setAddReturn(v).apply(e))
                  .orElse(new Function<V, Set<V>>(){
                     @Override
                     public Set<V> apply(V u){
                        Set<V> s = newsupplier.get();
                        s.add(u);
                        return s;
                     }
                  }.apply(v))
         );
      };
   }

   static <T> UnaryOperator<Set<T>> setAddReturn(T t){
      return s->{
         s.add(t);
         return s;
      };
   }
}

サンプル

Map<String, Set<String>> map = new HashMap<String, Set<String>>();

MapSetpush<String, String> m = MapSetpush.of(map, TreeSet<String>::new);

m.accept("a", "C");
m.accept("a", "A");
m.accept("a", "B");
m.accept("a", "A");

m.accept("b", "B");
m.accept("b", "C");
m.accept("b", "B");

System.out.println(map);

結果

{a=[A, B, C], b=[B, C]}

リストMap の リストに要素を追加(4)

oboe2uran.hatenablog.com

もっと短く書けるけど、メリットないか。。。

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.UnaryOperator;
/**
 * Map<K, List<V>> key存在自動チェック登録
 */
@FunctionalInterface
public interface MapListpush<K, V> extends Serializable{

   void accept(K k, V v);

   static <T> UnaryOperator<List<T>> listAddReturn(T t){
      return l->{
         l.add(t);
         return l;
      };
   }

   public static <K, V> MapListpush<K, V> of(Map<K, List<V>> m){
      return (k, v)->{
         m.put(k, Optional.ofNullable(m.get(k))
                  .map(e->listAddReturn(v).apply(e))
                  .orElseGet(()->new ArrayList<V>(Arrays.asList(v)))
         );
      };
   }
}

Stream boolean を集計

Stream 処理して単に、boolean を集計したい時、
1つでも true が発生、または全てが true になるかを求める時など、
forEach で、AtomicBoolean をセットするのは直感的だが、賢くはない。

map メソッドで、Boolean に変換して collect で集計するのがスマートである。

Stream<T> stream;
stream.map(t->{
    // TODO
    return  truefalse を返す。。。
})

1つでも true → trueを返す。

.collect( Collectors.reducing(false, Boolean::logicalOr) ); 

全部 true → trueを返す。

.collect( Collectors.reducing(true, Boolean::logicalAnd) );

Collectors.reducing は、これらの map の使い方によっては、以下のような集計もできる。

例)任意のリストで、ある条件で、map で Boolean を返すようにする。
例えば、30 より大きい値をカウントする。

int count = Arrays.asList(1, 12, 4, 23, 33, 43, 3, 55)
.stream().map(i->i > 30)
.collect(Collectors.reducing(0, t->t ? 1 : 0, (a, b)->a+b));

map メソッドで boolean に変換して、、
.collect(Collectors.reducing(0, t->t ? 1 : 0, (a, b)->a+b));

でも、あまりこんなカウントの方法でなくて、filter して Collectors.counting() を求めるのが
多くのやり方であろう。

リストMap の リストに要素を追加(3)

リストMap の リストに要素を追加する時、、 - Oboe吹きプログラマの黙示録

リストMap の リストに要素を追加(2) - Oboe吹きプログラマの黙示録

と書きましたが、、以下に落ち着きそうです。

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.UnaryOperator;
/**
 * Map<K, List<V>> key存在自動チェック登録
 */
@FunctionalInterface
public interface MapListpush<K, V> extends Serializable{

   void accept(K k, V v);

   static <T> UnaryOperator<List<T>> listAddReturn(T t){
      return l->{
         l.add(t);
         return l;
      };
   }

   public static <K, V> MapListpush<K, V> of(Map<K, List<V>> m){
      return (k, v)->{
         m.put(k,
            Optional.ofNullable(m.get(k))
            .map(e->listAddReturn(v).apply(e))
            .orElse(
               new Function<V, List<V>>(){
                  @Override
                  public List<V> apply(V u){
                     List<V> l = new ArrayList<V>();
                     l.add(u);
                     return l;
                  }
               }.apply(v)
            )
         );
      };
   }
}

使用例

Map<String, List<String>> map = new HashMap<>();

MapListpush<String, String> m = MapListpush.of(map);

m.accept("a", "A");
m.accept("a", "B");
m.accept("b", "C");
m.accept("b", "D");

System.out.println(map);

結果

{a=[A, B], b=[C, D]}

リストMap の リストに要素を追加(2)

昨日書いた、
oboe2uran.hatenablog.com
の続きです。
昨日書いたもの。。。

public static <T> UnaryOperator<List<T>> listAddReturn(T t){
   return l->{
      l.add(t);
      return l;
   };
}
Map<String, List<String>> map = new HashMap<>();

map.put("a", Optional.ofNullable(map.get("a"))
   .map(list->listAddReturn("A").apply(list))
   .orElse(listAddReturn("A").apply(new ArrayList<String>()))
);

これでは、listAddReturn を2回書いてしまいます。そこでとりあえず、、、

public static <K, V> void pushMap(Map<K, V> map, K k, UnaryOperator<V> u, Supplier<V> s){
   map.put(k, Optional.ofNullable(map.get(k))
               .map(e->u.apply(e))
               .orElse(u.apply(s.get()))
   );
}

単に Supplier を指定するようにしただけなので、以下のように書くことになるのですが、
毎回、Supplier を指定するのもどうかと思います。でもこれはこれで汎用性がありそうです。

pushMap(map, "b", listAddReturn("B"), ()->new ArrayList<String>());

Supplier 指定をしなくて済むものを用意します。
(上で書いた listAddReturn を使います)
List 限定になります。

public static <K, V> void pushMapList(Map<K, List<V>> map, K k, V v){
   map.put(k,
      Optional.ofNullable(map.get(k))
      .map(e->listAddReturn(v).apply(e))
      .orElse(
         new Function<V, List<V>>(){
            @Override
            public List<V> apply(V u){
               List<V> l = new ArrayList<V>();
               l.add(u);
               return l;
            }
         }.apply(v)
      )
   );
}

↑↑↑
なんか、短絡的というか、動物的なコーディングですが、、

pushMapList(map, "c", "C" );

これで、

Map<String, List<String>>  map;

に対して、
key が存在しなければ、List 生成して List に add してマップ PUSH
key が存在すれば、map から GETした List に add してマップ PUSH
というのが、少ない行で書けます。
メソッド名が、そういうことを処理するという表現であれば、そんなに可読性が悪くないと考えます。
。。。もうちょっと良い名前を考えよう。。。