فهرست منبع

new: Grid (fix #3)
new: CQuery#each(EachConsumer)
new: EachConsumer

Mariusz Sieroń 6 سال پیش
والد
کامیت
1d8324da31

+ 368 - 0
assira/src/main/java/net/ranides/assira/collection/Grid.java

@@ -0,0 +1,368 @@
+package net.ranides.assira.collection;
+
+import lombok.AccessLevel;
+import lombok.RequiredArgsConstructor;
+import net.ranides.assira.collection.lists.IntList;
+import net.ranides.assira.collection.lists.IntListUtils;
+import net.ranides.assira.collection.lists.ListUtils;
+import net.ranides.assira.collection.lookups.HashLookup;
+import net.ranides.assira.collection.lookups.Lookup;
+import net.ranides.assira.collection.maps.AMap;
+import net.ranides.assira.collection.query.CQuery;
+import net.ranides.assira.collection.query.CQueryAbstract;
+import net.ranides.assira.collection.sets.SetUtils;
+import net.ranides.assira.generic.CompareUtils;
+import net.ranides.assira.reflection.BeanModel;
+
+import java.util.AbstractMap.SimpleImmutableEntry;
+import java.util.*;
+import java.util.function.Function;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+import static java.util.stream.Collectors.toList;
+
+public class Grid<T> {
+
+    private final List<String> head;
+
+    private final Lookup<String> headmap;
+
+    private final List<List<T>> data;
+
+    private final GridHeader header;
+
+    private final GridRows rows;
+
+    private final GridColumns columns;
+
+    private Grid(List<String> head, Lookup<String> headmap, List<List<T>> data) {
+        this.head = head;
+        this.headmap = headmap;
+        this.data = data;
+        this.header = new GridHeader();
+        this.rows = new GridRows();
+        this.columns = new GridColumns();
+    }
+
+    private Grid(List<String> head, List<List<T>> data) {
+        this(head, initHead(head), data);
+    }
+
+    private static Lookup<String> initHead(List<String> head) {
+        HashLookup<String> out = new HashLookup<>();
+        for(int i=0, n=head.size(); i<n; i++) {
+            out.put(head.get(i), i);
+        }
+        return out;
+    }
+
+    public static <T> Grid<T> fromMap(List<? extends Map<String, T>> records) {
+        Set<String> headset = new LinkedHashSet<>();
+        records.stream().map(Map::keySet).forEach(headset::addAll);
+
+        List<String> head = new ArrayList<>(headset);
+        List<List<T>> data = records.stream().map(r -> head.stream().map((String key) -> r.get(key)).collect(toList())).collect(toList());
+
+        return new Grid<>(head, data);
+    }
+
+    public static Grid<Object> fromObjects(List<Object> rows) {
+        return fromMap(rows.stream().map(v -> BeanModel.typefor(v).values(v)).collect(toList()));
+    }
+
+    public static <T> Grid<T> fromRows(List<String> header, List<List<T>> rows) {
+        return new Grid<>(header, rows);
+    }
+
+    @Override
+    public String toString() {
+        return "Grid{ header =" + head +  ", data=" + data + "}";
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof Grid)) {
+            return false;
+        }
+        Grid<?> grid = (Grid<?>) o;
+        return Objects.equals(head, grid.head) && Objects.equals(data, grid.data);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(head, data);
+    }
+
+    public GridHeader header() {
+        return header;
+    }
+
+    public GridRows rows() {
+        return rows;
+    }
+
+    public GridColumns columns() {
+        return columns;
+    }
+
+    public Grid<T> rows(IntList selector) {
+        return new Grid<>(head, headmap, IntListUtils.map(selector, data::get));
+    }
+
+    public Grid<T> columns(IntList selector) {
+        return new Grid<>(IntListUtils.map(selector, head::get), data.stream().map(row -> IntListUtils.map(selector, row::get)).collect(toList()));
+    }
+
+    public List<List<T>> list() {
+        return data;
+    }
+
+    public CQuery<GridCursor> stream() {
+        return rows().unfold(r -> r);
+    }
+
+    public List<Map<String, T>> records() {
+        return ListUtils.map(data, GridRecord::new);
+    }
+
+    public T get(int row, int column) {
+        return data.get(row).get(column);
+    }
+
+    public T get(int row, String column) {
+        return data.get(row).get(headmap.getInt(column));
+    }
+
+    public void set(int row, int column, T value) {
+        data.get(row).set(column, value);
+    }
+
+    public void set(int row, String column, T value) {
+        data.get(row).set(headmap.getInt(column), value);
+    }
+
+    public Grid<T> sort(IntList selector) {
+        return new Grid<>(head, headmap, data.stream().sorted((a, b) -> rowcmp(selector, a, b)).collect(toList()));
+    }
+
+    public <R> Grid<R> map(Function<GridCursor, R> mapper) {
+        List<List<R>> out = new ArrayList<>(data.size());
+        for(int r=0, R=rows.size(); r<R; r++) {
+            List<T> irow = data.get(r);
+            List<R> orow = new ArrayList<>(irow.size());
+            for(int c=0, C=irow.size(); c<C; c++) {
+                orow.add( mapper.apply(new GridCursor(r,c)));
+            }
+            out.add(orow);
+        }
+        return new Grid<>(head, headmap, out);
+    }
+
+    private int rowcmp(IntList indexes, List<T> a, List<T> b) {
+        for(int i : indexes) {
+            int c = CompareUtils.cmp(a.get(i), b.get(i));
+            if(c != 0) {
+                return c;
+            }
+        }
+        return 0;
+    }
+
+    @RequiredArgsConstructor
+    private class GridRecord extends AMap<String, T> {
+
+        private final List<T> row;
+
+        @Override
+        public Set<Entry<String, T>> entrySet() {
+            return SetUtils.map(headmap.keySet(), key -> new SimpleImmutableEntry<>(key, row.get(headmap.getInt(key))) );
+        }
+
+        @Override
+        public int size() {
+            return head.size();
+        }
+
+        @Override
+        public boolean containsKey(Object key) {
+            return headmap.containsKey(key);
+        }
+
+        @Override
+        public boolean containsValue(Object value) {
+            return row.contains(value);
+        }
+
+        @Override
+        public T get(Object key) {
+            return row.get(headmap.get(key));
+        }
+    }
+
+    public class GridHeader extends CQueryAbstract<String> {
+
+        private GridHeader() {
+            // do nothing
+        }
+
+        @Override
+        public int size() {
+            return head.size();
+        }
+
+        @Override
+        public Stream<String> stream() {
+            return head.stream();
+        }
+
+        @Override
+        public Iterator<String> iterator() {
+            return head.iterator();
+        }
+
+    }
+
+    public class GridColumns extends CQueryAbstract<GridColumn> {
+
+        private final List<GridColumn> list;
+
+        private GridColumns() {
+            list = IntStream.range(0, size()).mapToObj(i -> new GridColumn(i)).collect(toList());
+        }
+
+        @Override
+        public int size() {
+            return head.size();
+        }
+
+        @Override
+        public Stream<GridColumn> stream() {
+            return list.stream();
+        }
+
+        @Override
+        public Iterator<GridColumn> iterator() {
+            return list.iterator();
+        }
+
+        public GridColumn get(int index) {
+            return list.get(index);
+        }
+
+    }
+
+    @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+    public class GridColumn extends CQueryAbstract<GridCursor> {
+
+        private final int index;
+
+        @Override
+        public int size() {
+            return data.size();
+        }
+
+        @Override
+        public Stream<GridCursor> stream() {
+            return IntStream.range(0, size()).mapToObj(row -> new GridCursor(row, index));
+        }
+
+        @Override
+        public Iterator<GridCursor> iterator() {
+            return stream().iterator();
+        }
+
+        public T get(int row) {
+            return data.get(row).get(index);
+        }
+
+    }
+
+    public class GridRows extends CQueryAbstract<GridRow> {
+
+        private List<GridRow> list = IntStream.range(0, size()).mapToObj(i -> new GridRow(i)).collect(toList());
+
+        private GridRows() {
+            // do nothing
+        }
+
+        @Override
+        public int size() {
+            return data.size();
+        }
+
+        @Override
+        public Stream<GridRow> stream() {
+            return list.stream();
+        }
+
+        @Override
+        public Iterator<GridRow> iterator() {
+            return list.iterator();
+        }
+
+        public GridRow get(int index) {
+            return list.get(index);
+        }
+
+    }
+
+    @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+    public class GridRow extends CQueryAbstract<GridCursor> {
+
+        private final int index;
+
+        @Override
+        public int size() {
+            return head.size();
+        }
+
+        @Override
+        public Stream<GridCursor> stream() {
+            return IntStream.range(0, size()).mapToObj(column -> new GridCursor(index, column));
+        }
+
+        @Override
+        public Iterator<GridCursor> iterator() {
+            return stream().iterator();
+        }
+
+        public T get(int column) {
+            return data.get(index).get(column);
+        }
+
+        public T get(String column) {
+            return data.get(index).get(headmap.getInt(column));
+        }
+    }
+
+
+    @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+    public class GridCursor {
+
+        private final int row;
+
+        private final int column;
+
+        public int row() {
+            return row;
+        }
+
+        public int column() {
+            return column;
+        }
+
+        public T get() {
+            return data.get(row).get(column);
+        }
+
+        public void set(T value) {
+            data.get(row).set(column, value);
+        }
+
+    }
+
+}

+ 3 - 0
assira/src/main/java/net/ranides/assira/collection/query/CQuery.java

@@ -12,6 +12,7 @@ import java.util.stream.Collector;
 import java.util.stream.Stream;
 import net.ranides.assira.collection.HashFunction;
 import net.ranides.assira.functional.EachFunction;
+import net.ranides.assira.functional.EachConsumer;
 
 /**
  *
@@ -90,6 +91,8 @@ public interface CQuery<T> extends Iterable<T> {
     CQuery<T> sort(Comparator<? super T> cmp);
     
     void each(Consumer<? super T> consumer);
+
+    void each(EachConsumer<? super T> consumer);
     
     void into(Collection<? super T> collection);
     

+ 9 - 0
assira/src/main/java/net/ranides/assira/collection/query/CQueryAbstract.java

@@ -20,6 +20,7 @@ import net.ranides.assira.collection.lists.ListUtils;
 import net.ranides.assira.collection.sets.CustomSet;
 import net.ranides.assira.collection.sets.HashSet;
 import net.ranides.assira.collection.sets.RBTreeSet;
+import net.ranides.assira.functional.EachConsumer;
 import net.ranides.assira.functional.EachFunction;
 import net.ranides.assira.functional.Fold;
 import net.ranides.assira.generic.CompareUtils;
@@ -209,6 +210,14 @@ public abstract class CQueryAbstract<T> implements CQuery<T> {
         stream().forEach(consumer);
     }
 
+    @Override
+    public final void each(EachConsumer<? super T> consumer) {
+        int index = 0;
+        for (T t : this) {
+            consumer.apply(index++, t);
+        }
+    }
+
     @Override
     public void into(Collection<? super T> collection) {
         stream().forEach(collection::add);

+ 17 - 0
assira/src/main/java/net/ranides/assira/functional/EachConsumer.java

@@ -0,0 +1,17 @@
+/*
+ * @author Ranides Atterwim <ranides@gmail.com>
+ * @copyright Ranides Atterwim
+ * @license WTFPL
+ * @url http://ranides.net/projects/assira
+ */
+package net.ranides.assira.functional;
+
+/**
+ *
+ * @author Ranides Atterwim <ranides@gmail.com>
+ */
+public interface EachConsumer<U> {
+    
+    void apply(int index, U value);
+    
+}

+ 172 - 0
assira/src/test/java/net/ranides/assira/collection/GridTest.java

@@ -0,0 +1,172 @@
+package net.ranides.assira.collection;
+
+import net.ranides.assira.collection.lists.IntArrayList;
+import net.ranides.assira.collection.lists.IntList;
+import net.ranides.assira.collection.lists.IntListUtils;
+import net.ranides.assira.collection.lists.IntRange;
+import net.ranides.assira.collection.maps.HashMap;
+import org.junit.Test;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.Arrays;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.Assert.*;
+
+public class GridTest {
+
+    List<? extends Map<String, Object>> source = Arrays.asList(
+            new Record<>().add("x", 2).add("y", 1).add("name", "first").add("description", "Curabitur vel felis.").add("time", asDateTime("2018-02-04 15:20")),
+            new Record<>().add("x", 234).add("y", 12).add("name", "second").add("description", "Duis viverra.").add("time", asDateTime("2018-02-04 15:20")),
+            new Record<>().add("x", 642).add("y", 31).add("name", "random").add("description", "Class aptent taciti.").add("time", asDateTime("2018-02-04 15:20")),
+            new Record<>().add("x", 832).add("y", 341).add("name", "big").add("description", "Sed lorem metus, iaculis at diam sit amet, blandit eleifend leo.").add("time", asDateTime("2018-03-04 17:30")),
+            new Record<>().add("x", 32).add("y", 1400).add("name", "virtual").add("description", "Fusce sed diam justo.").add("time", asDateTime("2018-03-24 18:15")),
+            new Record<>().add("x", 754).add("y", 1.5).add("name", "last").add("description", "ed finibus.").add("time", asDateTime("2018-02-12 11:05"))
+    );
+
+    private static ZonedDateTime asDateTime(String dt) {
+        return LocalDateTime.from(DateTimeFormatter.ISO_DATE_TIME.parse(dt.replace(' ','T'))).atZone(ZoneId.systemDefault());
+    }
+
+    @Test
+    public void asGrid() {
+        Grid<Object> grid = Grid.fromMap(source);
+
+        List<String> header = Arrays.asList("x", "y", "name", "description", "time");
+
+        List<Object> rows = Arrays.asList(
+                Arrays.asList(2, 1, "first", "Curabitur vel felis.", asDateTime("2018-02-04 15:20")),
+                Arrays.asList(234, 12, "second", "Duis viverra.", asDateTime("2018-02-04 15:20")),
+                Arrays.asList(642, 31, "random", "Class aptent taciti.", asDateTime("2018-02-04 15:20")),
+                Arrays.asList(832, 341, "big", "Sed lorem metus, iaculis at diam sit amet, blandit eleifend leo.", asDateTime("2018-03-04 17:30")),
+                Arrays.asList(32, 1400, "virtual", "Fusce sed diam justo.", asDateTime("2018-03-24 18:15")),
+                Arrays.asList(754, 1.5, "last", "ed finibus.", asDateTime("2018-02-12 11:05"))
+        );
+
+        assertEquals(header, grid.header().list());
+        assertEquals(rows, grid.list());
+    }
+
+    @Test
+    public void selectColumns() {
+        List<List<Object>> rows1 = Arrays.asList(
+                Arrays.asList(1, "first", "Curabitur vel felis."),
+                Arrays.asList(12, "second", "Duis viverra."),
+                Arrays.asList(31, "random", "Class aptent taciti."),
+                Arrays.asList(341, "big", "Sed lorem metus, iaculis at diam sit amet, blandit eleifend leo."),
+                Arrays.asList(1400, "virtual", "Fusce sed diam justo."),
+                Arrays.asList(1.5, "last", "ed finibus.")
+        );
+
+        Grid<Object> grid = Grid.fromMap(source);
+
+        assertEquals(rows1, grid.columns(new IntArrayList(new int[]{1,2,3})).list() );
+
+        assertEquals(Arrays.asList("y", "name","description"), grid.columns(new IntArrayList(new int[]{1,2,3})).header().list());
+
+        List<List<Object>> rows2 = Arrays.asList(
+                Arrays.asList("second"),Arrays.asList("random"),Arrays.asList("big"),Arrays.asList("virtual"),Arrays.asList("last")
+        );
+
+        List<String> header2 = Arrays.asList("name");
+
+        assertEquals(rows2, grid.columns(new IntArrayList(new int[]{2})).rows(new IntRange(1,6)).list());
+        assertEquals(header2, grid.columns(new IntArrayList(new int[]{2})).rows(new IntRange(1,6)).header().list());
+    }
+
+    @Test
+    public void sort() {
+
+        List<Object> sortName = Arrays.asList(
+                Arrays.asList(832, 341, "big", "Sed lorem metus, iaculis at diam sit amet, blandit eleifend leo.", asDateTime("2018-03-04 17:30")),
+                Arrays.asList(2, 1, "first", "Curabitur vel felis.", asDateTime("2018-02-04 15:20")),
+                Arrays.asList(754, 1.5, "last", "ed finibus.", asDateTime("2018-02-12 11:05")),
+                Arrays.asList(642, 31, "random", "Class aptent taciti.", asDateTime("2018-02-04 15:20")),
+                Arrays.asList(234, 12, "second", "Duis viverra.", asDateTime("2018-02-04 15:20")),
+                Arrays.asList(32, 1400, "virtual", "Fusce sed diam justo.", asDateTime("2018-03-24 18:15"))
+        );
+
+        List<Object> sortDateX = Arrays.asList(
+                Arrays.asList(2, 1, "first", "Curabitur vel felis.", asDateTime("2018-02-04 15:20")),
+                Arrays.asList(234, 12, "second", "Duis viverra.", asDateTime("2018-02-04 15:20")),
+                Arrays.asList(642, 31, "random", "Class aptent taciti.", asDateTime("2018-02-04 15:20")),
+                Arrays.asList(754, 1.5, "last", "ed finibus.", asDateTime("2018-02-12 11:05")),
+                Arrays.asList(832, 341, "big", "Sed lorem metus, iaculis at diam sit amet, blandit eleifend leo.", asDateTime("2018-03-04 17:30")),
+                Arrays.asList(32, 1400, "virtual", "Fusce sed diam justo.", asDateTime("2018-03-24 18:15"))
+        );
+
+        List<Object> sortDateName = Arrays.asList(
+                Arrays.asList(2, 1, "first", "Curabitur vel felis.", asDateTime("2018-02-04 15:20")),
+                Arrays.asList(642, 31, "random", "Class aptent taciti.", asDateTime("2018-02-04 15:20")),
+                Arrays.asList(234, 12, "second", "Duis viverra.", asDateTime("2018-02-04 15:20")),
+                Arrays.asList(754, 1.5, "last", "ed finibus.", asDateTime("2018-02-12 11:05")),
+                Arrays.asList(832, 341, "big", "Sed lorem metus, iaculis at diam sit amet, blandit eleifend leo.", asDateTime("2018-03-04 17:30")),
+                Arrays.asList(32, 1400, "virtual", "Fusce sed diam justo.", asDateTime("2018-03-24 18:15"))
+        );
+
+        Grid<Object> grid = Grid.fromMap(source);
+
+        assertEquals(sortName, grid.sort(new IntArrayList(new int[]{2})).list() );
+
+        assertEquals(sortDateX, grid.sort(new IntArrayList(new int[]{4,1})).list());
+
+        assertEquals(sortDateName, grid.sort(new IntArrayList(new int[]{4,2})).list());
+
+    }
+
+    @Test
+    public void numbers() {
+        List<? extends Map<String, Number>> list = Arrays.asList(
+                new Record<Number>().add("x", 16).add("y", 30).add("uid", 10701),
+                new Record<Number>().add("x", 84).add("y", 40).add("uid", 10702),
+                new Record<Number>().add("x", 73).add("y", 21).add("uid", 10825),
+                new Record<Number>().add("x", 27).add("y", 19).add("uid", 10649),
+                new Record<Number>().add("x", 83).add("y", 63).add("uid", 10383)
+        );
+
+        Grid<Number> g1 = Grid.fromMap(list);
+
+
+        assertEquals(21, g1.list().get(2).get(1));
+        assertEquals(21, g1.rows().get(2).get("y") );
+        assertEquals(21, g1.get(2, 1) );
+        assertEquals(21, g1.get(2, "y") );
+
+        Integer sum1 = g1.stream().map(c -> c.get().intValue()).fold(Integer::sum);
+        assertEquals(53716, sum1.intValue());
+
+        Integer sum2 = g1.stream().filter(c -> c.column()!=2).map(c -> c.get().intValue()).fold(Integer::sum);
+        assertEquals(456, sum2.intValue());
+
+        Integer sum3 = g1.stream().filter(c -> c.column()==1).map(c -> c.get().intValue()).fold(Integer::sum);
+        assertEquals(173, sum3.intValue());
+
+        List<List<String>> text = Arrays.asList(
+                Arrays.asList("16/1/1", "30/1/2", "10701/1/3"),
+                Arrays.asList("84/2/1", "40/2/2", "10702/2/3"),
+                Arrays.asList("73/3/1", "21/3/2", "10825/3/3"),
+                Arrays.asList("27/4/1", "19/4/2", "10649/4/3"),
+                Arrays.asList("83/5/1", "63/5/2", "10383/5/3")
+        );
+
+        Grid<String> g2 = g1.map(c -> c.get() + "/" + (c.row()+1) + "/" + (c.column()+1));
+
+        assertEquals(text, g2.list());
+    }
+
+    private static final class Record<V> extends LinkedHashMap<String, V> {
+
+        Record<V> add(String key, V value) {
+            put(key, value);
+            return this;
+        }
+
+    }
+
+}