/****************************************************************************** * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for * the specific language governing rights and limitations under the License. * * The Original Code is: Jsoda * The Initial Developer of the Original Code is: William Wong (williamw520@gmail.com) * Portions created by William Wong are Copyright (C) 2012 William Wong, All Rights Reserved. * ******************************************************************************/ package wwutil.sys; import java.util.*; /** * A simple class to add functional operations to make iteration easier. * * Import the interfaces and methods by: * import wwutil.sys.FnUtil.*; */ public class FnUtil { /** Callback function to work on a value and return a value of same type */ public static interface ApplyFn<V> { public V apply(V value); } /** Callback function to work on two parameters */ public static interface ApplyFn2<V1, V2> { public void apply(V1 value1, V2 value2); } /** Callback function to work on a value of type I and return a value of type R */ public static interface TransformFn<R, I> { public R apply(I value); } /** Callback function to check a value satisfying the predicate */ public static interface PredicateFn<V> { public boolean apply(V value); } /** Callback function to work on a value of type I and an accumulator of type R and return a value of type R */ public static interface FoldFn<R, I> { public R apply(R accumulator, I value); } public static interface FoldMapFn<R, K, V> { public R apply(R accumulator, K key, V value); } /** Iterate over the values of a map and transform the values in place. */ public static <V> Map<?, V> apply(Map<?, V> map, ApplyFn<V> fn) { for (Map.Entry<?, V> entry : map.entrySet()) entry.setValue( fn.apply( entry.getValue() ) ); return map; } /** Iterate over the values of a map and transform the values to a new map. */ public static <K, V> Map<K, V> map(Map<K, V> map, ApplyFn<V> fn) { Map<K, V> newMap = new HashMap<K, V>(); for (Map.Entry<K, V> entry : map.entrySet()) newMap.put(entry.getKey(), fn.apply( entry.getValue() ) ); return newMap; } /** Iterate over the values of a map and transform the values to a new map of different type. */ public static <K, V, R> Map<K, R> map(Map<K, V> map, TransformFn<R, V> fn) { Map<K, R> newMap = new HashMap<K, R>(); for (Map.Entry<K, V> entry : map.entrySet()) newMap.put(entry.getKey(), fn.apply( entry.getValue() ) ); return newMap; } /** Loop over a map's keys and values. */ public static <K, V> void loop(Map<K, V> map, ApplyFn2<K, V> fn) { for (Map.Entry<K, V> entry : map.entrySet()) fn.apply(entry.getKey(), entry.getValue()); } /** Fold the values of a map into an accumulator, e.g. sum. */ public static <R, V> R fold(R accumulator, Map<?, V> map, FoldFn<R, V> fn) { for (Map.Entry<?, V> entry : map.entrySet()) accumulator = fn.apply(accumulator, entry.getValue()); return accumulator; } /** Fold the entries of a map into an accumulator, e.g. sum. */ public static <R, K, V> R fold(R accumulator, Map<K, V> map, FoldMapFn<R, K, V> fn) { for (Map.Entry<K, V> entry : map.entrySet()) accumulator = fn.apply(accumulator, entry.getKey(), entry.getValue()); return accumulator; } /** Iterate over the values of a map and filter out the values to a new map. */ public static <K, V> Map<K, V> filter(Map<K, V> map, PredicateFn<V> fn) { Map<K, V> newMap = new HashMap<K, V>(); for (Map.Entry<K, V> entry : map.entrySet()) { if (!fn.apply(entry.getValue())) newMap.put(entry.getKey(), entry.getValue()); } return newMap; } /** Iterate over the values of a list and transform the values to a new list. */ public static <V> List<V> map(List<V> list, ApplyFn<V> fn) { List<V> newList = new ArrayList<V>(); for (V entry : list) newList.add(fn.apply( entry )); return newList; } /** Iterate over the values of a list and transform the values to a new list of different type. */ public static <R, V> List<R> map(List<V> list, TransformFn<R, V> fn) { List<R> newList = new ArrayList<R>(); for (V entry : list) newList.add(fn.apply( entry )); return newList; } /** Iterate over the values of a list and transform the values in place. */ public static <V> List<V> apply(List<V> list, ApplyFn<V> fn) { for (int i = 0; i < list.size(); i++) list.set(i, fn.apply(list.get(i))); return list; } /** Fold a list of values into an accumulator, e.g. sum. */ public static <R, V> R fold(R accumulator, List<V> list, FoldFn<R, V> fn) { for (V entry : list) accumulator = fn.apply(accumulator, entry); return accumulator; } public static <R, V> R fold(R accumulator, V[] array, FoldFn<R, V> fn) { return fold(accumulator, Arrays.asList(array), fn); } /** Iterate over the values of a list and filter the values to a new list. */ public static <V> List<V> filter(List<V> list, PredicateFn<V> fn) { List<V> newList = new ArrayList<V>(); for (V entry : list) { if (!fn.apply(entry)) newList.add(entry); } return newList; } /** Turns a list of arguments (or array) into a map. Arguments are listed as key1,value1, key2,value2, ... */ @SuppressWarnings("unchecked") public static <K,V> Map<K,V> asMap(K key1, V value1, Object... keyValPair) { Map<K,V> map = new HashMap<K,V>(); if (keyValPair.length % 2 != 0){ throw new IllegalArgumentException("Keys and values must be pairs."); } map.put(key1, value1); for (int i = 0; i < keyValPair.length; i += 2) { map.put((K)keyValPair[i], (V)keyValPair[i+1]); } return map; } // Unit test and samples /* public static void main(String[] args) { Map<String,String> map1 = FnUtil.asMap("k1", "v1", "k2", "v2"); Map<String,String> map2 = FnUtil.filter(map1, new PredicateFn<String>() { public boolean apply(String value) { return value.equals("v2"); } }); System.out.println( ReflectUtil.mapToStr(map1) ); System.out.println( ReflectUtil.mapToStr(map2) ); Map<String,String> map3 = FnUtil.map(map1, new ApplyFn<String>() { public String apply(String value) { return "new mapped " + value; } }); System.out.println( ReflectUtil.mapToStr(map3) ); FnUtil.apply(map1, new ApplyFn<String>() { public String apply(String value) { return "new applied " + value; } }); System.out.println( ReflectUtil.mapToStr(map1) ); Map<String,String> map4 = FnUtil.map(FnUtil.asMap("k1", 100, "k2", 200, "k3", 300), new TransformFn<String, Integer>() { public String apply(Integer value) { return "Transformed " + value; } }); System.out.println( ReflectUtil.mapToStr(map4) ); System.out.println( FnUtil.fold(0, map4, new FoldFn<Integer, String>() { public Integer apply(Integer sum, String value) { return sum + Integer.valueOf(value.split(" ")[1]); } }) ); System.out.println( FnUtil.fold(new StringBuilder(), map4, new FoldMapFn<StringBuilder, String, String>() { public StringBuilder apply(StringBuilder sb, String key, String value) { if (sb.length() > 0) sb.append(", "); sb.append(key).append(": ").append(value); return sb; } }).toString() ); List<Integer> list1 = FnUtil.map(Arrays.asList(100, 200, 300, 400), new ApplyFn<Integer>() { public Integer apply(Integer value) { return value + 10; } }); System.out.println( list1 ); int sum1 = FnUtil.fold(0, list1, new FoldFn<Integer, Integer>() { public Integer apply(Integer sum, Integer value) { return sum + value; } }); System.out.println( sum1 ); List<Float> list2 = FnUtil.map(Arrays.asList(100, 200, 300, 400), new TransformFn<Float, Integer>() { public Float apply(Integer value) { return value + 0.1f; } }); System.out.println( list2 ); FnUtil.apply(list1, new ApplyFn<Integer>() { public Integer apply(Integer value) { return value + 50; } }); System.out.println( list1 ); String concat1 = FnUtil.fold(new StringBuilder(), list1, new FoldFn<StringBuilder, Integer>() { public StringBuilder apply(StringBuilder sb, Integer value) { return sb.append(value).append(" "); } }).toString(); System.out.println( concat1 ); List<Float> list3 = FnUtil.filter(list2, new PredicateFn<Float>() { public boolean apply(Float value) { return value > 200; } }); System.out.println( list3 ); } */ }