From f92b7d5e72bfa13e5e00ed7cdd5ab635e615185e Mon Sep 17 00:00:00 2001 From: Ben Fry Date: Sun, 28 Apr 2013 10:49:06 -0400 Subject: [PATCH] port float changes over to int --- core/src/processing/data/IntHash.java | 306 ++++++++++++++++++-------- core/todo.txt | 3 + 2 files changed, 214 insertions(+), 95 deletions(-) diff --git a/core/src/processing/data/IntHash.java b/core/src/processing/data/IntHash.java index 095f2da9b..ea14624cd 100644 --- a/core/src/processing/data/IntHash.java +++ b/core/src/processing/data/IntHash.java @@ -2,6 +2,7 @@ package processing.data; import java.io.*; import java.util.HashMap; +import java.util.Iterator; import processing.core.PApplet; @@ -21,10 +22,14 @@ public class IntHash { private HashMap indices = new HashMap(); - static public IntHash fromTally(String[] list) { + /** + * Create a new object by counting the number of times each unique entry + * shows up in the specified String array. + */ + static public IntHash fromCount(String[] list) { IntHash outgoing = new IntHash(); for (String s : list) { - outgoing.increment(s); + outgoing.inc(s); } outgoing.crop(); return outgoing; @@ -47,6 +52,10 @@ public class IntHash { } + /** + * Create a new lookup with a specific size. This is more efficient than not + * specifying a size. Use it when you know the rough size of the thing you're creating. + */ public IntHash(int length) { count = 0; keys = new String[length]; @@ -54,23 +63,18 @@ public class IntHash { } - public IntHash(String[] k, int[] v) { - count = Math.min(k.length, v.length); - keys = new String[count]; - values = new int[count]; - System.arraycopy(k, 0, keys, 0, count); - System.arraycopy(v, 0, values, 0, count); - } - - - public IntHash(PApplet parent, String filename) { - String[] lines = parent.loadStrings(filename); + /** + * Read a set of entries from a Reader that has each key/value pair on + * a single line, separated by a tab. + */ + public IntHash(BufferedReader reader) { +// public IntHash(PApplet parent, String filename) { + String[] lines = PApplet.loadStrings(reader); keys = new String[lines.length]; values = new int[lines.length]; // boolean csv = (lines[0].indexOf('\t') == -1); for (int i = 0; i < lines.length; i++) { -// if (lines[i].trim().length() != 0) { // String[] pieces = csv ? Table.splitLineCSV(lines[i]) : PApplet.split(lines[i], '\t'); String[] pieces = PApplet.split(lines[i], '\t'); if (pieces.length == 2) { @@ -100,13 +104,38 @@ public class IntHash { } - /** - * Return the internal array being used to store the keys. Allocated but - * unused entries will be removed. This array should not be modified. - */ - public String[] keys() { - crop(); - return keys; +// /** +// * Return the internal array being used to store the keys. Allocated but +// * unused entries will be removed. This array should not be modified. +// */ +// public String[] keys() { +// crop(); +// return keys; +// } + + + public Iterable keys() { + return new Iterable() { + + @Override + public Iterator iterator() { + return new Iterator() { + int index = -1; + + public void remove() { + removeIndex(index); + } + + public String next() { + return key(++index); + } + + public boolean hasNext() { + return index+1 < size(); + } + }; + } + }; } @@ -114,7 +143,14 @@ public class IntHash { * Return a copy of the internal keys array. This array can be modified. */ public String[] keyArray() { - String[] outgoing = new String[count]; + return keyArray(null); + } + + + public String[] keyArray(String[] outgoing) { + if (outgoing == null || outgoing.length != count) { + outgoing = new String[count]; + } System.arraycopy(keys, 0, outgoing, 0, count); return outgoing; } @@ -125,52 +161,112 @@ public class IntHash { } - public int[] values() { - crop(); - return values; + public Iterable values() { + return new Iterable() { + + @Override + public Iterator iterator() { + return new Iterator() { + int index = -1; + + public void remove() { + removeIndex(index); + } + + public Integer next() { + return value(++index); + } + + public boolean hasNext() { + return index+1 < size(); + } + }; + } + }; } + /** + * Create a new array and copy each of the values into it. + */ public int[] valueArray() { - int[] outgoing = new int[count]; - System.arraycopy(values, 0, outgoing, 0, count); - return outgoing; + return valueArray(null); } - public int get(String what) { - int index = index(what); + /** + * Fill an already-allocated array with the values (more efficient than + * creating a new array each time). If 'array' is null, or not the same + * size as the number of values, a new array will be allocated and returned. + */ + public int[] valueArray(int[] array) { + if (array == null || array.length != size()) { + array = new int[count]; + } + System.arraycopy(values, 0, array, 0, count); + return array; + } + + + /** + * Return a value for the specified key. + */ + public int get(String key) { + int index = index(key); if (index == -1) return 0; return values[index]; } - public void set(String who, int amount) { - int index = index(who); + public void set(String key, int amount) { + int index = index(key); if (index == -1) { - create(who, amount); + create(key, amount); } else { values[index] = amount; } } - public void add(String who, int amount) { - int index = index(who); + /** Increase the value of a specific key by 1. */ + public void inc(String key) { + inc(key, 1); + } + + + public void inc(String key, int amount) { + int index = index(key); if (index == -1) { - create(who, amount); + create(key, amount); } else { values[index] += amount; } } - public void increment(String who) { - int index = index(who); - if (index == -1) { - create(who, 1); - } else { - values[index]++; + /** Decrease the value of a key by 1. */ + public void dec(String key) { + inc(key, -1); + } + + + public void dec(String key, int amount) { + inc(key, -amount); + } + + + public void mul(String key, int amount) { + int index = index(key); + if (index != -1) { + values[index] *= amount; + } + } + + + public void div(String key, int amount) { + int index = index(key); + if (index != -1) { + values[index] /= amount; } } @@ -183,12 +279,8 @@ public class IntHash { protected void create(String what, int much) { if (count == keys.length) { - String ktemp[] = new String[count << 1]; - System.arraycopy(keys, 0, ktemp, 0, count); - keys = ktemp; - int vtemp[] = new int[count << 1]; - System.arraycopy(values, 0, vtemp, 0, count); - values = vtemp; + keys = PApplet.expand(keys); + values = PApplet.expand(values); } indices.put(what, new Integer(count)); keys[count] = what; @@ -197,28 +289,15 @@ public class IntHash { } - public void print() { - write(new PrintWriter(System.out)); + public void remove(String key) { + removeIndex(index(key)); } - public void write(PrintWriter writer) { - for (int i = 0; i < count; i++) { - writer.println(keys[i] + "\t" + values[i]); - } - writer.flush(); - } - - - public void remove(String which) { - removeIndex(index(which)); - } - - - public void removeIndex(int which) { + public void removeIndex(int index) { //System.out.println("index is " + which + " and " + keys[which]); - indices.remove(keys[which]); - for (int i = which; i < count-1; i++) { + indices.remove(keys[index]); + for (int i = index; i < count-1; i++) { keys[i] = keys[i+1]; values[i] = values[i+1]; indices.put(keys[i], i); @@ -242,28 +321,17 @@ public class IntHash { } + /** + * Sort the keys alphabetically (ignoring case). Uses the value as a + * tie-breaker (only really possible with a key that has a case change). + */ public void sortKeys() { - Sort s = new Sort() { - @Override - public int size() { - return count; - } + sortImpl(true, false); + } - @Override - public float compare(int a, int b) { - int result = keys[a].compareToIgnoreCase(keys[b]); - if (result != 0) { - return result; - } - return values[b] - values[a]; - } - @Override - public void swap(int a, int b) { - IntHash.this.swap(a, b); - } - }; - s.run(); + public void sortKeysReverse() { + sortImpl(true, true); } @@ -271,15 +339,16 @@ public class IntHash { * Sort by values in descending order (largest value will be at [0]). */ public void sortValues() { - sortValues(true); + sortImpl(false, false); } - /** - * Sort by values. Identical values will use the keys as tie-breaker. - * @param descending true to put the largest value at position 0. - */ - public void sortValues(final boolean descending) { + public void sortValuesReverse() { + sortImpl(false, true); + } + + + protected void sortImpl(final boolean useKeys, final boolean reverse) { Sort s = new Sort() { @Override public int size() { @@ -288,11 +357,19 @@ public class IntHash { @Override public float compare(int a, int b) { - int diff = values[b] - values[a]; - if (diff == 0) { + int diff = 0; + if (useKeys) { + diff = values[a] - values[b]; + if (diff == 0) { + diff = keys[a].compareToIgnoreCase(keys[b]); + } + } else { // sort values diff = keys[a].compareToIgnoreCase(keys[b]); + if (diff == 0) { + return values[a] - values[b]; + } } - return descending ? diff : -diff; + return reverse ? diff : -diff; } @Override @@ -302,4 +379,43 @@ public class IntHash { }; s.run(); } -} \ No newline at end of file + + + /** Returns a duplicate copy of this object. */ + public IntHash copy() { + IntHash outgoing = new IntHash(count); + System.arraycopy(keys, 0, outgoing.keys, 0, count); + System.arraycopy(values, 0, outgoing.values, 0, count); + for (int i = 0; i < count; i++) { + outgoing.indices.put(keys[i], i); + } + return outgoing; + } + + + /** + * Write tab-delimited entries out to + * @param writer + */ + public void write(PrintWriter writer) { + for (int i = 0; i < count; i++) { + writer.println(keys[i] + "\t" + values[i]); + } + writer.flush(); + } + + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(getClass().getName() + " size= " + size() + " { "); + for (int i = 0; i < size(); i++) { + if (i != 0) { + sb.append(", "); + } + sb.append("\"" + keys[i] + "\": " + values[i]); + } + sb.append(" }"); + return sb.toString(); + } +} diff --git a/core/todo.txt b/core/todo.txt index 63e6a0ad4..b07724256 100644 --- a/core/todo.txt +++ b/core/todo.txt @@ -44,6 +44,9 @@ _ getIntArray() or toIntArray() (also for JSONArray) _ add indent= as option for XML, JSON save _ not doing print() methods, since alternatives are more descriptive _ println(obj) and obj.write(System.out) are both better +_ using Iterable for rows(), keys(), etc +_ generally more efficient for Table, but slower for FloatHash and IntHash +_ could use an int array instead, but a bit hokey in places List: remove(), append(), index(), etc all use values removeIndex(index) is the other