Skip to main content

Data Arrays

To make common data manipulation operations simple, datacore provides the DataArray abstraction, which is a proxied wrapper around a regular list with a large set of additional functions.

Creation​

Data arrays are mainly present in two places.

Via dc.useArray​

The most common route for using data arrays is the dc.useArray hook, which takes in a regular array of data, converts it to a data array, performs operations on it, and then converts it back to a regular array:

return function View() {
// Start with some data you want to process...
const books = dc.useQuery("#books and @page");
// Use `dc.useArray` to get a data array for processing.
const groupedBooks = dc.useArray(books, array =>
array.sort(book => book.$name)
.groupBy(book => book.value("genre")));

// Then render it.
return <dc.List rows={groupedBooks} />;
}

Via dc.array​

You can also directly make DataArrays via the utility function dc.array(data), which accepts a regular array as input and produces a data array.

return function View() {
// da is a `DataArray`.
const da = dc.array([1, 2, 3]);
// da2 is still a `DataArray`.
const da2 = da.map(x => x + 4).limit(2);

// To get a regular array back, use `.array()`.
const data = da2.array();
}

Indexing and Swizzling​

Data arrays support regular indexing just like normal arrays (like array[0]), but importantly, they also support query-language-style "swizzling": if you index into a data array with a field name (like array.field), it automatically maps every element in the array to field, flattening field if it itself is also an array.

const data = dc.array(dc.query("#books and @page"));

data.$name // => List of all book names.
data.$ctime // => List of all book created times.

Raw Interface​

The full interface for the data array implementation is provided below for reference:

/** A function which maps an array element to some value. */
export type ArrayFunc<T, O> = (elem: T, index: number, arr: T[]) => O;

/** A function which compares two types. */
export type ArrayComparator<T> = (a: T, b: T) => number;

/**
* Proxied interface which allows manipulating array-based data. All functions on a data array produce a NEW array
* (i.e., the arrays are immutable).
*/
export interface DataArray<T> {
/** The total number of elements in the array. */
length: number;

/** Filter the data array down to just elements which match the given predicate. */
where(predicate: ArrayFunc<T, boolean>): DataArray<T>;
/** Alias for 'where' for people who want array semantics. */
filter(predicate: ArrayFunc<T, boolean>): DataArray<T>;

/** Map elements in the data array by applying a function to each. */
map<U>(f: ArrayFunc<T, U>): DataArray<U>;
/** Map elements in the data array by applying a function to each, then flatten the results to produce a new array. */
flatMap<U>(f: ArrayFunc<T, U[]>): DataArray<U>;
/** Mutably change each value in the array, returning the same array which you can further chain off of. */
mutate(f: ArrayFunc<T, any>): DataArray<any>;

/** Limit the total number of entries in the array to the given value. */
limit(count: number): DataArray<T>;
/**
* Take a slice of the array. If `start` is undefined, it is assumed to be 0; if `end` is undefined, it is assumed
* to be the end of the array.
*/
slice(start?: number, end?: number): DataArray<T>;
/** Concatenate the values in this data array with those of another iterable / data array / array. */
concat(other: Iterable<T>): DataArray<T>;

/** Return the first index of the given (optionally starting the search) */
indexOf(element: T, fromIndex?: number): number;
/** Return the first element that satisfies the given predicate. */
find(pred: ArrayFunc<T, boolean>): T | undefined;
/** Find the index of the first element that satisfies the given predicate. Returns -1 if nothing was found. */
findIndex(pred: ArrayFunc<T, boolean>, fromIndex?: number): number;
/** Returns true if the array contains the given element, and false otherwise. */
includes(element: T): boolean;

/**
* Return a string obtained by converting each element in the array to a string, and joining it with the
* given separator (which defaults to ', ').
*/
join(sep?: string): string;

/**
* Return a sorted array sorted by the given key; an optional comparator can be provided, which will
* be used to compare the keys in leiu of the default dataview comparator.
*/
sort<U>(key: ArrayFunc<T, U>, direction?: "asc" | "desc", comparator?: ArrayComparator<U>): DataArray<T>;

/**
* Return an array where elements are grouped by the given key; the resulting array will have objects of the form
* { key: <key value>, rows: DataArray }.
*/
groupBy<U>(key: ArrayFunc<T, U>, comparator?: ArrayComparator<U>): DataArray<{ key: U; rows: DataArray<T> }>;

/**
* Return distinct entries. If a key is provided, then rows with distinct keys are returned.
*/
distinct<U>(key?: ArrayFunc<T, U>, comparator?: ArrayComparator<U>): DataArray<T>;

/** Return true if the predicate is true for all values. */
every(f: ArrayFunc<T, boolean>): boolean;
/** Return true if the predicate is true for at least one value. */
some(f: ArrayFunc<T, boolean>): boolean;
/** Return true if the predicate is FALSE for all values. */
none(f: ArrayFunc<T, boolean>): boolean;

/** Return the first element in the data array. Returns undefined if the array is empty. */
first(): T;
/** Return the last element in the data array. Returns undefined if the array is empty. */
last(): T;

/** Map every element in this data array to the given key, and then flatten it.*/
to(key: string): DataArray<any>;
/**
* Recursively expand the given key, flattening a tree structure based on the key into a flat array. Useful for handling
* hierarchical data like tasks with 'subtasks'.
*/
expand(key: string): DataArray<any>;

/** Run a lambda on each element in the array. */
forEach(f: ArrayFunc<T, void>): void;

/** Convert this to a plain javascript array. */
array(): T[];

/** Allow iterating directly over the array. */
[Symbol.iterator](): Iterator<T>;

/** Map indexes to values. */
[index: number]: any;
/** Automatic flattening of fields. Equivalent to implicitly calling `array.to("field")` */
[field: string]: any;
}