API Docs for:
Show:

File: src/core/array_data.js

window.multigraph.util.namespace("window.multigraph.core", function (ns) {
    "use strict";

    /**
     * @module multigraph
     * @submodule core
     */

    /**
     * @class ArrayData
     * @for ArrayData
     * @constructor
     * @param {array} columns A array of DataVariables
     * @param {array} stringArray A array of strings which will later be parsed into DataValues
     */
    ns.ArrayData = window.jermaine.Model(function () {
        var ArrayData = this;

        this.isA(ns.Data);
        this.hasAn("array");
        this.hasA("stringArray");
        this.isBuiltWith("columns", "stringArray", function () {
            this.init();
            this.addListener("listenerAdded", function (event) {
                if (event.targetType === "dataReady") {
                    var data = this.array();
                    event.listener(data[0][0], data[data.length-1][0]);
                }
            });
        });

        /**
         * 
         *
         * @method getIterator
         * @param {string array} columnIDs
         * @param {DataValue} min
         * @param {DataValue} max
         * @param {Integer} buffer
         * @author jrfrimme
         */
        this.respondsTo("getIterator", function (columnIds, min, max, buffer) {
            return ArrayData.getArrayDataIterator(this, columnIds, min, max, buffer);
        });

        /**
         * Determines the upper and lower bounds of a column in a dataset.
         *
         * @method getBounds
         * @param {Integer} columnNumber Column in the dataset to have its bounds determined.
         * @return {array} Array in the form: [lowerBound, upperBound].
         * @author jrfrimme
         */
        this.respondsTo("getBounds", function (columnNumber) {
            var data = this.array(),
                min = data[0][columnNumber],
                max = min,
                i;

            for (i = 1; i < data.length; i++) {
                if (data[i][columnNumber] < min) {
                    min = data[i][columnNumber];
                }
                if (data[i][columnNumber] > max) {
                    max = data[i][columnNumber];
                }
            }

            return [min, max];
        });

        /**
         * @method onReady
         * @param callback
         */

        /**
         * @method getArrayDataIterator
         * @static
         * @param {ArrayData} arrayData
         * @param {string array} columnIDs
         * @param {DataValue} min
         * @param {DataValue} max
         * @param {Integer} buffer
         * @return iter
         * @author jrfrimme
         */
        ArrayData.getArrayDataIterator = function (arrayData, columnIds, min, max, buffer) {
            var i, j,
                firstIndex, lastIndex,
                currentIndex,
                columnIndices,
                array = arrayData.array();

            buffer = buffer || 0;

            // columnIds argument should be an array of strings
            if (Object.prototype.toString.apply(columnIds) !== "[object Array]") {
                throw new Error("ArrayData: getIterator method requires that the first parameter be an array of strings");
            } else {
                for (i = 0; i < columnIds.length; ++i) {
                    if (typeof(columnIds[i]) !== "string") {
                        throw new Error("ArrayData: getIterator method requires that the first parameter be an array of strings");
                    }
                }
            }

            //min,max arguments should be data values
            if (!ns.DataValue.isInstance(min) || !ns.DataValue.isInstance(max)) {
                throw new Error("ArrayData: getIterator method requires the second and third argument to be number values");
            }

            //buffer argument should be an integer
            if (typeof(buffer) !== "number") {
                throw new Error("ArrayData: getIterator method requires last argument to be an integer");
            }

            // if we have no data, return an empty iterator
            if (array.length === 0) {
                return {
                    "next"    : function () {},
                    "hasNext" : function () { return false; }
                };
            }

            // find the index of the first row in the array whose column0 value is >= min
            for (firstIndex = 0; firstIndex < array.length; ++firstIndex) {
                if (array[firstIndex][0].ge(min)) {
                    break;
                }
            }
            // back up 'buffer' steps
            firstIndex = firstIndex - buffer;
            if (firstIndex < 0) {
                firstIndex = 0;
            }
            
            // find the index of the last row in the array whose column0 value is <= max
            if (firstIndex === array.length-1) {
                lastIndex = firstIndex;
            } else {
                for (lastIndex = firstIndex; lastIndex < array.length-1; ++lastIndex) {
                    if (array[lastIndex+1][0].gt(max)) {
                        break;
                    }
                }
            }
            // move forward 'buffer' steps
            lastIndex = lastIndex + buffer;
            if (lastIndex > array.length-1) {
                lastIndex = array.length-1;
            }

            columnIndices = [];
            for (j = 0; j < columnIds.length; ++j) {
                var k = arrayData.columnIdToColumnNumber(columnIds[j]);
                columnIndices.push( k );
            }

            currentIndex = firstIndex;
                
            return {
                next : function () {
                    var projection = [],
                        i;
                    if (currentIndex > lastIndex) {
                        return null;
                    }
                    for (i = 0; i < columnIndices.length; ++i) {
                        projection.push(array[currentIndex][columnIndices[i]]);
                    }
                    ++currentIndex;
                    return projection;
                },
                hasNext : function () {
                    return currentIndex <= lastIndex;
                }
            };
        };

        /**
         * @method textToDataValuesArray
         * @static
         * @param {array} dataVariableArray
         * @param {string} text
         * @return {array} dataValues
         * @author jrfrimme
         * @todo If the number of comma-separated values on the current line is not the same as the number of columns in the metadata, should throw an error.
         */
        ArrayData.textToDataValuesArray = function (dataVariableArray, text) {
            //IMPORTANT NOTE: dataVariableArray is a plain javascript array of DataVariable instances; it
            //is NOT a jermaine attr_list.
            var dataValues = [],
                lines = text.split("\n"),
                i;
            for (i = 0; i < lines.length; ++i) {
                if (/\d/.test(lines[i])) { // skip line unless it contains a digit
                    var stringValuesThisRow = lines[i].split(/\s*,\s*/),
                        dataValuesThisRow = [],
                        j;
                    if (stringValuesThisRow.length === dataVariableArray.length) {
                        for (j = 0; j < stringValuesThisRow.length; ++j) {
                            dataValuesThisRow.push(ns.DataValue.parse(dataVariableArray[j].type(), stringValuesThisRow[j]));
                        }
                        dataValues.push( dataValuesThisRow );
                    //} else {
                        // we get here if the number of comma-separated values on the current line
                        // (lines[i]) is not the same as the number of columns in the metadata.  This
                        // should probably throw an error, or something like that.  For now, though, we
                        // just ignore it.
                        //console.log('bad line: ' + lines[i]);
                    }
                }
            }
            return dataValues;
        };

        /**
         * @method textToStringArray
         * @static
         * @param {string} text 
         * @return {array} stringValues
         * @author jrfrimme
         */
        ArrayData.textToStringArray = function (dataVariables, text) {
            var stringValues = [],
                lines = text.split("\n"),
                stringValuesThisRow,
                numColumns,
                i;

            // clean up each line
            for (i = 0; i < lines.length; ++i) {
                lines[i] = lines[i]
                    .replace(/^\s+/,     "")   // remove leading whitespace
                    .replace(/\s+$/,     "")   // remove trailing whitespace
                    .replace(/\s*,\s*/g, ",")  // remove any whitespace next to commas
                    .replace(/\s+/g,     ","); // replace any remaining whitespace runs with a comma
                // now line consists of comma-separated values, with no whitespace
            }

            for (i = 0; i < lines.length; ++i) {
                if (/\d/.test(lines[i])) { // skip line unless it contains a digit
                    numColumns = lines[i].split(/,/).length;
                    break;
                }
            }

            for (i = 0; i < lines.length; ++i) {
                if (/\d/.test(lines[i])) { // skip line unless it contains a digit
                    stringValuesThisRow = lines[i].split(/,/);
                    if (stringValuesThisRow.length === numColumns) {
                        stringValues.push( stringValuesThisRow );
                    } else {
                        throw new Error("Data Parsing Error: The line '" + lines[i] + "' has " + stringValuesThisRow.length + " data columns when it requires " + numColumns + " columns");
                    }
                }
            }
            return stringValues;
        };

        /**
         * Parses an array of strings into an array of DataValues.
         * 
         * @method textToStringArray
         * @static
         * @param {array} dataVariableArray plain javascript array of DataVariables
         * @param {array} stringArray plain javascript array of strings
         * @return {array} plain javascript array of DataValue instances
         * @author jrfrimme
         */
        ArrayData.stringArrayToDataValuesArray = function (dataVariableArray, stringArray) {
            //IMPORTANT NOTE: dataVariableArray is a plain javascript array of DataVariable instances; it
            //is NOT a jermaine attr_list.

            var dataValues = [],
                dataValuesThisRow,
                i,
                j;

            for (i = 0; i < stringArray.length; ++i) {
                dataValuesThisRow = [];
                for (j = 0; j < stringArray[i].length; ++j) {
                    dataValuesThisRow.push(ns.DataValue.parse(dataVariableArray[j].type(), stringArray[i][j]));
                }
                dataValues.push( dataValuesThisRow );
            }
            return dataValues;
        };

    });
});