bc81851998-03-23Fredrik Noring // Table.pmod by Fredrik Noring, 1998
24b58e1998-06-26Fredrik Noring // $Id: Table.pmod,v 1.9 1998/06/26 16:43:34 noring Exp $
bc81851998-03-23Fredrik Noring  #define TABLE_ERR(msg) throw(({ "(Table) "+msg+"\n", backtrace() })) class table { static private mapping fieldmap; static private array table, fields, types; static private array|int remap(array|string|int cs, int|void forgive) { array v = ({}); int ap = arrayp(cs); if(!ap) cs = ({ cs }); foreach(cs, string|int f) if(zero_type(intp(f)?f:fieldmap[lower_case(f)])) { if(!forgive) TABLE_ERR("Unknown field '"+f+"'"); } else v += ({ intp(f)?f:fieldmap[lower_case(f)] }); return ap?v:v[0]; } object copy(array|void tab, array|void fie, array|void typ) { return object_program(this_object())(tab||table,fie||fields,typ||types); } string encode() { return encode_value(([ "table":table,"fields":fields,"types":types ])); } object decode(string s) { mapping m = decode_value(s); return copy(m->table, m->fields, m->types); } mixed cast(string type) { switch(type) { case "array": return copy_value(table); #if 0 // This works only in Pike 0.6. case "string": return ASCII->encode(this_object()); #endif } } array _indices() { return copy_value(fields); } array _values() { return copy_value(table); } int _sizeof() { return sizeof(table); } object reverse() { return copy(predef::reverse(table), fields, types); } array col(int|string c) { return copy_value(column(table, remap(c))); } array row(int r) { return copy_value(table[r]); } array `[](int|string c) { return col(c); } int `==(object t) {
a294d21998-03-23Fredrik Noring  return (equal(Array.map(fields, lower_case), Array.map(indices(t), lower_case)) && equal(table, values(t)));
bc81851998-03-23Fredrik Noring  } object append_bottom(object t) { if(!equal(Array.map(indices(t), lower_case), Array.map(fields, lower_case))) TABLE_ERR("Table fields are not equal."); return copy(table+values(t), fields, types); } object append_right(object t) { if(sizeof(t) != sizeof(table)) TABLE_ERR("Table sizes are not equal."); array v = values(t); for(int r = 0; r < sizeof(table); r++)
24b58e1998-06-26Fredrik Noring  v[r] = table[r] + v[r]; return copy(v, fields+indices(t), types+t->all_types());
bc81851998-03-23Fredrik Noring  }
1c13ae1998-03-25Fredrik Noring  static private mixed op_col(function f, int|string c, mixed ... args)
bc81851998-03-23Fredrik Noring  { c = remap(c); mixed x = table[0][c]; for(int r = 1; r < sizeof(table); r++) f(x, table[r][c], @args); return x; } mixed sum_col(int|string c) { return `+(@column(table, remap(c))); } mixed average_col(int|string c) { return sum_col(c)/sizeof(table); } mixed min_col(int|string c) { return op_col(min, c); } mixed max_col(int|string c) { return op_col(max, c); } object select(int|string ... cs) { array t = ({}); cs = remap(cs); for(int r = 0; r < sizeof(table); r++) t += ({ rows(table[r], cs) }); return copy(t, rows(fields, cs), rows(types, cs)); } object remove(int|string ... cs) { return select(@remap(fields) - remap(cs, 1)); }
4acdcb1998-05-10Fredrik Noring  object where(array(int|string)|int|string cs, function f, mixed ... args)
bc81851998-03-23Fredrik Noring  { array t = ({});
ff9f9d1998-06-18Fredrik Noring  f = f || lambda(mixed x) { return x; };
bc81851998-03-23Fredrik Noring  cs = remap(arrayp(cs)?cs:({ cs })); foreach(table, mixed row) if(f(@rows(row, cs), @args)) t += ({ row }); return copy(t, fields, types); }
fbfdd91998-05-09Fredrik Noring  object group(mapping(int|string:function)|function f, mixed ... args)
bc81851998-03-23Fredrik Noring  { if(!sizeof(table)) return this_object();
fbfdd91998-05-09Fredrik Noring  if(functionp(f)) {
bc8bfd1998-05-10Fredrik Noring  if(!arrayp(args[0])) args[0] = ({ args[0] });
fbfdd91998-05-09Fredrik Noring  f = mkmapping(args[0], allocate(sizeof(args[0]), f)); args = args[1..]; }
bc81851998-03-23Fredrik Noring  mapping m = ([]); array cs = remap(indices(f)); f = mkmapping(cs, values(f)); array(int) keys = indices(fields) - cs; foreach(table, array row) { string key = encode_value(rows(row, keys)); if(array a = m[key]) foreach(cs, int c) a[c] = f[c](a[c], row[c], @args); else m[key] = copy_value(row); } return copy(values(m), fields, types); } object sum(int|string ... cs) {
fbfdd91998-05-09Fredrik Noring  return group(`+, cs);
bc81851998-03-23Fredrik Noring  } object distinct(int|string ... cs) { if(!sizeof(cs)) return sum();
a294d21998-03-23Fredrik Noring  array f = remap(fields) - remap(cs);
bc81851998-03-23Fredrik Noring  mapping m = mkmapping(f, Array.map(f, lambda() { return lambda(mixed x1, mixed x2) { return x1; }; }));
fbfdd91998-05-09Fredrik Noring  return group(m);
bc81851998-03-23Fredrik Noring  }
fbfdd91998-05-09Fredrik Noring  object map(function f, array(int|string)|int|string cs, mixed ... args)
bc81851998-03-23Fredrik Noring  { int ap = arrayp(cs); array t = copy_value(table); if(!catch(cs = remap(ap?cs:({ cs })))) { for(int r = 0; r < sizeof(t); r++) { mixed v = f(@rows(t[r], cs), @args); if(arrayp(v)) for(int i = 0; i < sizeof(v); i++) t[r][cs[i]] = v[i]; else t[r][cs[0]] = v; } } return copy(t, fields, types); } object sort(int|string ... cs) { if(!sizeof(cs)) return this_object(); int c; array t = copy_value(table); if(!catch(c = remap(cs[-1]))) { mapping m = ([]); for(int r = 0; r < sizeof(t); r++) { mixed d; if(!m[d = t[r][c]]) m[d] = ({ t[r] }); else m[d] += ({ t[r] }); } array i = indices(m), v = values(m); predef::sort(i, v); t = v*({}); } return copy(t, fields, types)->sort(@cs[0..(sizeof(cs)-2)]); } object rsort(int|string ... cs) { return sort(@cs)->reverse(); }
fbfdd91998-05-09Fredrik Noring  object limit(int n)
bc81851998-03-23Fredrik Noring  { return copy(table[0..(n-1)], fields, types); }
fbfdd91998-05-09Fredrik Noring  object rename(string|int from, string to)
bc81851998-03-23Fredrik Noring  {
fbfdd91998-05-09Fredrik Noring  array a = copy_value(fields); a[remap(from)] = to; return copy(table, a, types);
bc81851998-03-23Fredrik Noring  } mapping type(int|string c, void|mapping m) { if(query_num_arg() == 2) types[remap(c)] = copy_value(m); return copy_value(types[remap(c)]); }
24b58e1998-06-26Fredrik Noring  array all_types() { return copy_value(types); }
bc81851998-03-23Fredrik Noring  void create(array(array) tab, array(string) fie, array|void typ) { if(!arrayp(tab)) TABLE_ERR("Table not array"); if(!arrayp(fie)) TABLE_ERR("Fields not array"); if(sizeof(tab) && sizeof(tab[0]) != sizeof(fie)) TABLE_ERR("Table and field sizes differ"); foreach(fie, string s) if(!stringp(s)) TABLE_ERR("Field name not string"); table = copy_value(tab); fields = copy_value(fie); types = allocate(sizeof(fie)); if(typ) for(int i = 0; i < sizeof(fields); i++) if(!typ[i] || mappingp(typ[i])) types[i] = copy_value(typ[i]); else TABLE_ERR("Field type not mapping"); array a = indices(allocate(sizeof(fields))); fieldmap = mkmapping(Array.map(fields, lower_case), a); } } object Separated = class { static private string _string(mixed x) { return (string)x; } object decode(string s, void|mapping options) { string rowsep = options->rowsep||"\n"; string colsep = options->colsep||"\t"; array t = Array.map(s/rowsep, `/, colsep); return table(t[1..], t[0], options->types); } mixed encode(object t, void|mapping options) {
7b74091998-06-24Fredrik Noring  options = options || ([]);
bc81851998-03-23Fredrik Noring  string rowsep = options->rowsep||"\n"; string colsep = options->colsep||"\t"; return Array.map(({ indices(t) }) + values(t), lambda(array r, string colsep) { return Array.map(r, _string)*colsep; }, colsep)*rowsep; } }(); object ASCII = class { object decode(string s, void|mapping options) { // Yet to be done return 0; } string encode(object t, void|mapping options) { options = options || ([]); mapping sizes = ([]); array fields = indices(t); string indent = String.strmult(" ", options->indent); t = t->copy(({ fields }) + values(t)); foreach(indices(fields), string field)
fbfdd91998-05-09Fredrik Noring  t = (t->map(lambda(mixed m, string field, mapping sizes) { m = (string)m; sizes[field] = max(sizeof(m), sizes[field]); return m; }, field, field, sizes)-> map(lambda(string s, string size, int num) { return sprintf("%"+(num?"":"-")+size+"s", s); }, field, (string)sizes[field], (t->type(field)||([]))->type == "num"));
bc81851998-03-23Fredrik Noring  string l = (indent+"-"+ Array.map(values(sizes), lambda(int n) { return String.strmult("-", n); })*"---"+"-"); array table = values(t); return (indent+" "+table[0]*" "+"\n"+l+"\n"+ Array.map(table[1..], lambda(array row, string indent) { return indent+" "+row*" "; },
fbfdd91998-05-09Fredrik Noring  indent)*"\n"+(sizeof(table)>1?"\n":"")+l+"\n");
bc81851998-03-23Fredrik Noring  } }(); // Experimental object SQL = class { object decode(array t, void|mapping options) { // Yet to be done return 0; } array encode(object t, void|mapping options) { options = options||([]); string tablename = options->tablename||"sql_encode_default_table"; array queries = ({}); string fields = indices(t)*", "; foreach(values(t), array row) queries += ({ "insert into "+tablename+" ("+fields+") " "values("+ Array.map(row, lambda(mixed x) { return "'"+(string)x+"'"; })*", "+ ")" }); return queries; } }(); // Huh? Experimental help.. string help() { return ("This is the experimental help system for Table.pmod.\n" "Table.pmod contains the following classes and objects:\n" "\n" " Table.table(array(array) table, array(string) fields, " "array(mapping) types)\n" " Table.ASCII.encode\n" " Table.ASCII.decode\n" " Table.Separated.decode\n" " Table.Separated.encode\n" " Table.SQL.decode\n" " Table.SQL.encode\n"); }