636275856908b6b71134527c9c410acb82682d08
kent
  Fri Feb 20 15:29:24 2015 -0800
Adding several routines that work across a column into library including one to see if column is entirely numeric, another to figure out how wide it is, and a third to sort on a column.

diff --git src/lib/fieldedTable.c src/lib/fieldedTable.c
index 5037bcd..ac0d492 100644
--- src/lib/fieldedTable.c
+++ src/lib/fieldedTable.c
@@ -53,30 +53,128 @@
 struct fieldedRow *fr;
 lmAllocVar(lm, fr);
 lmAllocArray(lm, fr->row, rowSize);
 fr->id = id;
 int i;
 for (i=0; i<rowSize; ++i)
     fr->row[i] = lmCloneString(lm, row[i]);
 
 /* Add it to end of list using cursor to avoid slReverse hassles. */
 *(table->cursor) = fr;
 table->cursor = &fr->next;
 
 return fr;
 }
 
+int fieldedTableMaxColChars(struct fieldedTable *table, int colIx)
+/* Calculate the maximum number of characters in a cell for a column */
+{
+if (colIx >= table->fieldCount)
+    errAbort("fieldedTableMaxColChars on %d, but only have %d columns", colIx, table->fieldCount);
+int max = strlen(table->fields[colIx]) + 1;
+struct fieldedRow *fr;
+for (fr = table->rowList; fr != NULL; fr = fr->next)
+    {
+    char *val = fr->row[colIx];
+    if (val != NULL)
+        {
+	int len = strlen(val);
+	if (len > max)
+	   max = len;
+	}
+    }
+return max;
+}
+
+boolean fieldedTableColumnIsNumeric(struct fieldedTable *table, int fieldIx)
+/* Return TRUE if field has numeric values wherever non-null */
+{
+struct fieldedRow *fr;
+boolean anyVals = FALSE;
+for (fr = table->rowList; fr != NULL; fr = fr->next)
+    {
+    char *s = fr->row[fieldIx];
+    if (s != NULL)
+        {
+	anyVals = TRUE;
+	if (!isNumericString(s))
+	    return FALSE;
+	}
+    }
+return anyVals;
+}
+
+static int slPairCmpNumbers(const void *va, const void *vb)
+/* Compare slPairs where name is interpreted as floating point number */
+{
+const struct slPair *a = *((struct slPair **)va);
+const struct slPair *b = *((struct slPair **)vb);
+double aVal = atof(a->name);
+double bVal = atof(b->name);
+double diff = aVal - bVal;
+if (diff < 0)
+    return -1;
+else if (diff > 0)
+    return 1;
+else
+    return 0;
+}
+
+
+void fieldedTableSortOnField(struct fieldedTable *table, char *field, boolean doReverse)
+/* Sort on field.  Distinguishes between numerical and text fields appropriately.  */
+{
+/* Figure out field position */
+int fieldIx = stringArrayIx(field, table->fields, table->fieldCount);
+if (fieldIx < 0)
+    fieldIx = 0;
+boolean fieldIsNumeric = fieldedTableColumnIsNumeric(table, fieldIx);
+
+/* Make up pair list in local memory which points to rows */
+struct lm *lm = lmInit(0);
+struct slPair *pairList=NULL, *pair;
+struct fieldedRow *fr;
+for (fr = table->rowList; fr != NULL; fr = fr->next)
+    {
+    char *val = emptyForNull(fr->row[fieldIx]);
+    lmAllocVar(lm, pair);
+    pair->name = val;
+    pair->val = fr;
+    slAddHead(&pairList, pair);
+    }
+slReverse(&pairList);  
+
+/* Sort this list. */
+if (fieldIsNumeric)
+    slSort(&pairList, slPairCmpNumbers);
+else
+    slSort(&pairList, slPairCmpCase);
+if (doReverse)
+    slReverse(&pairList);
+
+/* Convert rowList to have same order. */
+struct fieldedRow *newList = NULL;
+for (pair = pairList; pair != NULL; pair = pair->next)
+    {
+    fr = pair->val;
+    slAddHead(&newList, fr);
+    }
+slReverse(&newList);
+table->rowList = newList;
+lmCleanup(&lm);
+}
+
 struct fieldedTable *fieldedTableFromTabFile(char *fileName, char *reportFileName, 
     char *requiredFields[], int requiredCount)
 /* Read table from tab-separated file with a #header line that defines the fields.  Ensures
  * all requiredFields (if any) are present.  The reportFileName is just used for error reporting and 
  * should be NULL for most purposes.  This is used by edwSubmit though which
  * first copies to a local file, and we want to report errors from the remote file. 
  * We do know the remote file exists at least, because we just copied it. */
 {
 /* Open file with fileName */
 struct lineFile *lf = lineFileOpen(fileName, TRUE);
 
 /* Substitute in reportFileName for error reporting */
 if (reportFileName != NULL)
     {
     if (differentString(reportFileName, fileName))