From d706b1a15f1a929e4d726490b9e9e25d0ed122fa Mon Sep 17 00:00:00 2001 From: jheer Date: Fri, 23 Apr 2021 16:26:02 +0200 Subject: [PATCH] feat: Add rename verb. (#150) --- docs/api/verbs.md | 31 ++++++++++++++++++++- src/query/verb.js | 3 +++ src/table/transformable.js | 12 +++++++++ src/verbs/index.js | 2 ++ src/verbs/rename.js | 8 ++++++ test/query/query-test.js | 36 ++++++++++++++++++------- test/query/verb-test.js | 24 ++++++++++++++++- test/query/verb-to-ast-test.js | 31 ++++++++++++++++++++- test/verbs/rename-test.js | 49 ++++++++++++++++++++++++++++++++++ 9 files changed, 184 insertions(+), 12 deletions(-) create mode 100644 src/verbs/rename.js create mode 100644 test/verbs/rename-test.js diff --git a/docs/api/verbs.md b/docs/api/verbs.md index cfd62057..4dd0ba44 100644 --- a/docs/api/verbs.md +++ b/docs/api/verbs.md @@ -12,7 +12,7 @@ title: Verbs \| Arquero API Reference * [orderby](#orderby), [unorder](#unorder) * [rollup](#rollup), [count](#count) * [sample](#sample) - * [select](#select), [relocate](#relocate) + * [select](#select), [relocate](#relocate), [rename](#rename) * [reify](#reify) * [Join Verbs](#joins) * [cross](#cross) @@ -273,6 +273,35 @@ table.relocate({ colA: 'newA', colB: 'newB' }, { after: 'colC' }) ``` +
# +table.rename(columns) · [Source](https://github.com/uwdata/arquero/blob/master/src/verbs/rename.js) + +Rename one or more columns, preserving column order. + +* *columns*: One or more rename objects with current column names as keys and new column names as values. + +*Examples* + +```js +// rename colA to colA2 +table.rename({ colA: 'colA2' }) +``` + +```js +// rename 'old col' to 'new col' +table.rename({ 'old col': 'new col' }) +``` + +```js +// rename colA and colB +table.rename({ colA: 'colA2', colB: 'colB2' }) +``` + +```js +// rename colA and colB, alternate syntax +table.rename({ colA: 'colA2' }, { colB: 'colB2' }) +``` +
# table.reify([indices]) · [Source](https://github.com/uwdata/arquero/blob/master/src/table/column-table.js) diff --git a/src/query/verb.js b/src/query/verb.js index 1ce6b6e9..3fd167bd 100644 --- a/src/query/verb.js +++ b/src/query/verb.js @@ -164,6 +164,9 @@ export const Verbs = { props: { before: SelectionList, after: SelectionList } } ]), + rename: createVerb('rename', [ + { name: 'columns', type: SelectionList } + ]), rollup: createVerb('rollup', [ { name: 'values', type: ExprObject } ]), diff --git a/src/table/transformable.js b/src/table/transformable.js index 70180098..85e3fa16 100644 --- a/src/table/transformable.js +++ b/src/table/transformable.js @@ -178,6 +178,18 @@ export default class Transformable { return this.__relocate(this, toArray(columns), options); } + /** + * Rename one or more columns, preserving column order. + * @param {...Select} columns One or more rename objects with current + * column names as keys and new column names as values. + * @return {this} A new table with renamed columns. + * @example table.rename({ oldName: 'newName' }) + * @example table.rename({ a: 'a2', b: 'b2' }) + */ + rename(...columns) { + return this.__rename(this, columns.flat()); + } + /** * Rollup a table to produce an aggregate summary. * Often used in conjunction with {@link Transformable#groupby}. diff --git a/src/verbs/index.js b/src/verbs/index.js index 542c52be..e401bfb3 100644 --- a/src/verbs/index.js +++ b/src/verbs/index.js @@ -10,6 +10,7 @@ import __semijoin from './join-filter'; import __lookup from './lookup'; import __pivot from './pivot'; import __relocate from './relocate'; +import __rename from './rename'; import __rollup from './rollup'; import __sample from './sample'; import __select from './select'; @@ -47,6 +48,7 @@ export default { __lookup, __pivot, __relocate, + __rename, __rollup, __sample, __select, diff --git a/src/verbs/rename.js b/src/verbs/rename.js new file mode 100644 index 00000000..1cbc2cad --- /dev/null +++ b/src/verbs/rename.js @@ -0,0 +1,8 @@ +import _select from '../engine/select'; +import resolve from '../helpers/selection'; + +export default function(table, columns) { + const map = new Map(); + table.columnNames(x => (map.set(x, x), 0)); + return _select(table, resolve(table, columns, map)); +} \ No newline at end of file diff --git a/test/query/query-test.js b/test/query/query-test.js index 6c213ef2..3c9bfdd2 100644 --- a/test/query/query-test.js +++ b/test/query/query-test.js @@ -10,7 +10,7 @@ import { field, func } from './util'; const { count, dedupe, derive, filter, groupby, orderby, reify, rollup, select, sample, ungroup, unorder, - relocate, impute, fold, pivot, spread, unroll, + relocate, rename, impute, fold, pivot, spread, unroll, cross, join, semijoin, antijoin, concat, union, except, intersect } = Verbs; @@ -429,6 +429,24 @@ tape('Query evaluates orderby verbs', t => { t.end(); }); +tape('Query evaluates reify verbs', t => { + const dt = table({ + foo: [0, 1, 2, 3], + bar: [1, 1, 0, 0] + }).filter(d => d.foo < 1); + + tableEqual( + t, + Query.from( + new Query([ reify() ]).toObject() + ).evaluate(dt), + { foo: [0], bar: [1] }, + 'reify query result' + ); + + t.end(); +}); + tape('Query evaluates relocate verbs', t => { const a = [1], b = [2], c = [3], d = [4]; const dt = table({ a, b, c, d }); @@ -458,19 +476,19 @@ tape('Query evaluates relocate verbs', t => { t.end(); }); -tape('Query evaluates reify verbs', t => { - const dt = table({ - foo: [0, 1, 2, 3], - bar: [1, 1, 0, 0] - }).filter(d => d.foo < 1); +tape('Query evaluates rename verbs', t => { + const a = [1], b = [2], c = [3], d = [4]; + const dt = table({ a, b, c, d }); tableEqual( t, Query.from( - new Query([ reify() ]).toObject() + new Query([ + rename({ d: 'w', a: 'z' }) + ]).toObject() ).evaluate(dt), - { foo: [0], bar: [1] }, - 'reify query result' + { z: a, b, c, w: d }, + 'rename query result' ); t.end(); diff --git a/test/query/verb-test.js b/test/query/verb-test.js index 168d9cda..e45f68e7 100644 --- a/test/query/verb-test.js +++ b/test/query/verb-test.js @@ -9,7 +9,7 @@ import { field, func } from './util'; const { count, dedupe, derive, filter, groupby, orderby, reify, rollup, sample, select, ungroup, unorder, - relocate, impute, pivot, unroll, join, concat + relocate, rename, impute, pivot, unroll, join, concat } = Verbs; function test(t, verb, expect, msg) { @@ -215,6 +215,28 @@ tape('relocate verb serializes to object', t => { t.end(); }); +tape('rename verb serializes to object', t => { + test(t, + rename([{ foo: 'bar' }]), + { + verb: 'rename', + columns: [{ foo: 'bar' }] + }, + 'serialized rename verb' + ); + + test(t, + rename([{ foo: 'bar' }, { baz: 'bop' }]), + { + verb: 'rename', + columns: [{ foo: 'bar' }, { baz: 'bop' }] + }, + 'serialized rename verb' + ); + + t.end(); +}); + tape('rollup verb serializes to object', t => { const verb = rollup({ count: op.count(), diff --git a/test/query/verb-to-ast-test.js b/test/query/verb-to-ast-test.js index b150daf5..7365a460 100644 --- a/test/query/verb-to-ast-test.js +++ b/test/query/verb-to-ast-test.js @@ -8,7 +8,7 @@ import { const { count, dedupe, derive, filter, groupby, orderby, reify, rollup, sample, select, ungroup, unorder, - relocate, impute, pivot, unroll, join, concat + relocate, rename, impute, pivot, unroll, join, concat } = Verbs; function toAST(verb) { @@ -296,6 +296,35 @@ tape('relocate verb serializes to AST', t => { t.end(); }); +tape('rename verb serializes to AST', t => { + t.deepEqual( + toAST(rename([{ foo: 'bar' }])), + { + type: 'Verb', + verb: 'rename', + columns: [ + { type: 'Column', name: 'foo', as: 'bar' } + ] + }, + 'ast rename verb' + ); + + t.deepEqual( + toAST(rename([{ foo: 'bar' }, { baz: 'bop' }])), + { + type: 'Verb', + verb: 'rename', + columns: [ + { type: 'Column', name: 'foo', as: 'bar' }, + { type: 'Column', name: 'baz', as: 'bop' } + ] + }, + 'ast rename verb' + ); + + t.end(); +}); + tape('rollup verb serializes to AST', t => { const verb = rollup({ count: op.count(), diff --git a/test/verbs/rename-test.js b/test/verbs/rename-test.js new file mode 100644 index 00000000..05151fe2 --- /dev/null +++ b/test/verbs/rename-test.js @@ -0,0 +1,49 @@ +import tape from 'tape'; +import tableEqual from '../table-equal'; +import { table } from '../../src'; + +tape('rename renames columns', t => { + const data = { + a: [1, 3, 5, 7], + b: [2, 4, 6, 8], + c: 'abcd'.split('') + }; + + tableEqual(t, + table(data).rename({ a: 'z'}), + { z: data.a, b: data.b, c: data.c }, + 'renamed data, single column' + ); + + tableEqual(t, + table(data).rename({ a: 'z', b: 'y' }), + { z: data.a, y: data.b, c: data.c }, + 'renamed data, multiple columns' + ); + + t.deepEqual( + table(data).rename({ a: 'z', c: 'x' }).columnNames(), + ['z', 'b', 'x'], + 'renamed data, preserves order' + ); + + tableEqual(t, + table(data).rename('a', 'b'), + data, + 'renamed data, no rename' + ); + + tableEqual(t, + table(data).rename(), + data, + 'renamed data, no arguments' + ); + + tableEqual(t, + table(data).rename({ a: 'z'}, { c: 'x' }), + { z: data.a, b: data.b, x: data.c }, + 'renamed data, multiple arguments' + ); + + t.end(); +}); \ No newline at end of file