You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@arrow.apache.org by we...@apache.org on 2018/07/16 23:29:00 UTC

[arrow] branch master updated: ARROW-2771: [JS] Add row proxy object accessor

This is an automated email from the ASF dual-hosted git repository.

wesm pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/arrow.git


The following commit(s) were added to refs/heads/master by this push:
     new ebc8dab  ARROW-2771: [JS] Add row proxy object accessor
ebc8dab is described below

commit ebc8dabec52fa03c4424e56edf9a236417b26d84
Author: Brian Hulette <hu...@gmail.com>
AuthorDate: Mon Jul 16 19:28:54 2018 -0400

    ARROW-2771: [JS] Add row proxy object accessor
    
    Creates a custom `RowProxy` class based on the schema whenever a new `Table` is constructed, and yields instances of it in `get` and the iterator.
    
    Now you could use the following code to compute the mean of the `score` column for all rows where `name === target`.
    
    ``` javascript
    let sum = 0, count = 0;
    for (let row of table) {
        if (row.name === target) {
            sum += row.score;
            count += 1;
        }
    }
    return sum/count;
    ```
    
    Author: Brian Hulette <hu...@gmail.com>
    
    Closes #2197 from TheNeuralBit/row-proxy and squashes the following commits:
    
    a1be00fb <Brian Hulette> Move row proxy to StructView
    2aa90779 <Brian Hulette> Store RowProxy constructor rather than re-writing a method
    57af6433 <Brian Hulette> Table.get and Table iterator now return row proxy objects
---
 js/src/ipc/writer/binary.ts |  2 +-
 js/src/table.ts             |  1 +
 js/src/util/node.ts         |  8 ++++----
 js/src/vector/nested.ts     | 29 ++++++++++++++++++++++++++++-
 js/test/unit/table-tests.ts | 15 +++++++++++----
 5 files changed, 45 insertions(+), 10 deletions(-)

diff --git a/js/src/ipc/writer/binary.ts b/js/src/ipc/writer/binary.ts
index 166d4b8..2bab428 100644
--- a/js/src/ipc/writer/binary.ts
+++ b/js/src/ipc/writer/binary.ts
@@ -82,7 +82,7 @@ export function* serializeFile(table: Table) {
     // Then yield the footer metadata (not aligned)
     ({ metadataLength, buffer } = serializeFooter(new Footer(dictionaryBatches, recordBatches, table.schema)));
     yield buffer;
-    
+
     // Last, yield the footer length + terminating magic arrow string (aligned)
     buffer = new Uint8Array(magicAndPadding);
     new DataView(buffer.buffer).setInt32(0, metadataLength, platformIsLittleEndian);
diff --git a/js/src/table.ts b/js/src/table.ts
index 8144c98..e3be9bb 100644
--- a/js/src/table.ts
+++ b/js/src/table.ts
@@ -111,6 +111,7 @@ export class Table implements DataFrame {
         this.length = this.batchesUnion.length;
         this.numCols = this.batchesUnion.numCols;
     }
+
     public get(index: number): Struct['TValue'] {
         return this.batchesUnion.get(index)!;
     }
diff --git a/js/src/util/node.ts b/js/src/util/node.ts
index e588cb7..8e58f6b 100644
--- a/js/src/util/node.ts
+++ b/js/src/util/node.ts
@@ -21,10 +21,10 @@ export class PipeIterator<T> implements IterableIterator<T> {
         let write = (err?: any) => {
             stream['removeListener']('error', write);
             stream['removeListener']('drain', write);
-            if (err) return this.throw(err);
+            if (err) { return this.throw(err); }
             if (stream['writable']) {
                 do {
-                    if ((res = this.next()).done) break;
+                    if ((res = this.next()).done) { break; }
                 } while (emit(stream, encoding, res.value));
             }
             return wait(stream, res && res.done, write);
@@ -56,10 +56,10 @@ export class AsyncPipeIterator<T> implements AsyncIterableIterator<T> {
         let write = async (err?: any) => {
             stream['removeListener']('error', write);
             stream['removeListener']('drain', write);
-            if (err) return this.throw(err);
+            if (err) { return this.throw(err); }
             if (stream['writable']) {
                 do {
-                    if ((res = await this.next()).done) break;
+                    if ((res = await this.next()).done) { break; }
                 } while (emit(stream, encoding, res.value));
             }
             return wait(stream, res && res.done, write);
diff --git a/js/src/vector/nested.ts b/js/src/vector/nested.ts
index fe9f7e9..6d1bd4a 100644
--- a/js/src/vector/nested.ts
+++ b/js/src/vector/nested.ts
@@ -128,9 +128,36 @@ export class DenseUnionView extends UnionView<DenseUnion> {
     }
 }
 
+type RowProxy = {[name: string]: any};
+interface RowViewConstructor<T extends RowProxy = RowProxy> {
+    readonly prototype: T & RowView;
+    new (data: Data<SparseUnion> & NestedView<any>, children?: Vector<any>[], rowIndex?: number): T & RowView;
+}
+
 export class StructView extends NestedView<Struct> {
+    private RowView: RowViewConstructor;
+
+    constructor(data: Data<Struct>, children?: Vector<any>[]) {
+        super(data, children);
+
+        // Make a customized RowView that includes proxies for
+        class RowProxy extends RowView {}
+
+        const proto = RowProxy.prototype;
+
+        data.type.children.forEach(function (f, i) {
+            Object.defineProperty(proto, f.name, {
+                get: function () {
+                    return (this as any as RowView).get(i);
+                },
+                enumerable: true
+            });
+        });
+
+        this.RowView = (RowProxy as any);
+    }
     protected getNested(self: StructView, index: number) {
-        return new RowView(self as any, self._children, index);
+        return new self.RowView(self as any, self._children, index);
     }
     protected setNested(self: StructView, index: number, value: any): void {
         let idx = -1, len = self.numChildren, child: Vector | null;
diff --git a/js/test/unit/table-tests.ts b/js/test/unit/table-tests.ts
index 32b15fa..39b27f9 100644
--- a/js/test/unit/table-tests.ts
+++ b/js/test/unit/table-tests.ts
@@ -86,13 +86,20 @@ describe(`Table`, () => {
             });
             test(`gets expected values`, () => {
                 for (let i = -1; ++i < values.length;) {
-                    expect(table.get(i).toArray()).toEqual(values[i]);
+                    const row = table.get(i);
+                    const expected = values[i];
+                    expect(row.f32).toEqual(expected[F32]);
+                    expect(row.i32).toEqual(expected[I32]);
+                    expect(row.dictionary).toEqual(expected[DICT]);
                 }
             });
             test(`iterates expected values`, () => {
                 let i = 0;
                 for (let row of table) {
-                    expect(row.toArray()).toEqual(values[i++]);
+                    const expected = values[i++];
+                    expect(row.f32).toEqual(expected[F32]);
+                    expect(row.i32).toEqual(expected[I32]);
+                    expect(row.dictionary).toEqual(expected[DICT]);
                 }
             });
             describe(`scan()`, () => {
@@ -252,8 +259,8 @@ describe(`Table`, () => {
                 let idx = 0, expected_row;
                 for (let row of selected) {
                     expected_row = values[idx++];
-                    expect(row.get(0)).toEqual(expected_row[F32]);
-                    expect(row.get(1)).toEqual(expected_row[DICT]);
+                    expect(row.f32).toEqual(expected_row[F32]);
+                    expect(row.dictionary).toEqual(expected_row[DICT]);
                 }
             });
             test(`table.toString()`, () => {