/* global onmessage:true */
import defined from "terriajs-cesium/Source/Core/defined";
import sortedIndices from "../../../Core/sortedIndices";
/**
* Create combined arrays from arrays of column values, eg. [[values1, values2, values3], [values4, values5]].
* The first columns of each array must be of the same type (in the above example, values1 and values4).
* These are combined and sorted into a single column.
* Then the subsequent columns are added, filling with null where missing. (This could be an option in future.)
* Eg. if the values of each col are: values1=[1,3]; values2=[10,30]; values3=[100,300]; values4=[1,2]; values5=[-1,-2];
* then the resulting array of column values are, in order, [1,2,3]; [10,null,30]; [100,null,300]; [-1,-2,null].
* @param {Array[]} valueArrays See description above.
* @return {Array[]} The synthesized values which could be passed to a table structure.
*/
function combineValueArrays(valueArrays) {
if (!defined(valueArrays) || valueArrays.length < 1) {
return;
}
let combinedValueArrays = [];
// Start by copying the first set of columns into the result.
const firstArray = valueArrays[0];
for (let j = 0; j < firstArray.length; j++) {
const values = firstArray[j];
combinedValueArrays.push(values.slice());
}
// Then add the subsequent sets of x-columns to the end of the first result column,
// add nulls to the end of the other existing columns,
// add nulls to the start of the new columns,
// and add them to the end of the result.
for (let i = 1; i < valueArrays.length; i++) {
const currentValueArray = valueArrays[i];
const currentFirstArray = currentValueArray[0];
const preExistingValuesLength = combinedValueArrays[0].length;
combinedValueArrays[0] = combinedValueArrays[0].concat(currentFirstArray);
const empty1 = new Array(currentFirstArray.length); // elements are undefined.
for (let k = 1; k < combinedValueArrays.length; k++) {
combinedValueArrays[k] = combinedValueArrays[k].concat(empty1);
}
const empty2 = new Array(preExistingValuesLength); // elements are undefined.
for (let j = 1; j < currentValueArray.length; j++) {
const values = currentValueArray[j];
combinedValueArrays.push(empty2.concat(values));
}
}
// Sort by the first column.
combinedValueArrays = sortByFirst(combinedValueArrays);
combinedValueArrays = combineRepeated(combinedValueArrays);
return combinedValueArrays;
}
/**
* Eg. sortByFirst([['b', 'a', 'c'], [1, 2, 3]]) = [['a', 'b', 'c'], [2, 1, 3]].
* @param {Array[]} valueArrays The array of arrays of values to sort.
* @return {Array[]} The values sorted by the first column.
*/
function sortByFirst(valueArrays) {
const firstValues = valueArrays[0];
const indices = sortedIndices(firstValues);
return valueArrays.map(function(values) {
return indices.map(function(sortedIndex) {
return values[sortedIndex];
});
});
}
/**
* @param {Array[]} sortedJulianDateOrValueArrays The array of arrays of values to combine. These must be sortedByFirst. Dates must be JulianDates.
* @param {Integer} [firstColumnType] Eg. VarType.TIME.
* @return {Array[]} The values, with any repeats in the first column combined into one. Dates are converted to ISO8601 string representation.
*
* Eg.
* var x = [['a', 'b', 'b', 'c'], [1, 2, undefined, 3], [4, undefined, 5, undefined]];
* combineRepeated(x);
* # x is [['a', 'b', 'c'], [1, 2, 3], [4, 5, undefined]].
*/
function combineRepeated(sortedValueArrays) {
const result = new Array(sortedValueArrays.length);
for (let i = 0; i < result.length; i++) {
result[i] = [sortedValueArrays[i][0]];
}
for (let j = 1; j < sortedValueArrays[0].length; j++) {
if (sortedValueArrays[0][j] === sortedValueArrays[0][j - 1]) {
const currentIndex = result[0].length - 1;
for (let i = 0; i < result.length; i++) {
if (result[i][currentIndex] === undefined) {
result[i][currentIndex] = sortedValueArrays[i][j];
}
}
} else {
for (let i = 0; i < result.length; i++) {
result[i].push(sortedValueArrays[i][j]);
}
}
}
return result;
}
/**
* Convert an array of column values, with column names, to an array of row values.
* @param {Array[]} columnValueArrays Array of column values, eg. [[1,2,3], [4,5,6]].
* @param {String[]} columnNames Array of column names, eg ['x', 'y'].
* @return {Array[]} Array of rows, starting with the column names, eg. [['x', 'y'], [1, 4], [2, 5], [3, 6]].
*/
function toArrayOfRows(columnValueArrays, columnNames) {
if (columnValueArrays.length < 1) {
return;
}
const rows = columnValueArrays[0].map(function(value0, rowIndex) {
return columnValueArrays.map(function(values) {
return values[rowIndex];
});
});
rows.unshift(columnNames);
return rows;
}
onmessage = function(event) {
const valueArrays = event.data.values.map(valuesArray =>
valuesArray.map(values => Array.prototype.slice.call(values))
); // Convert from typed arrays.
const nameArrays = event.data.names;
const combinedValues = combineValueArrays(valueArrays);
const rows = toArrayOfRows(combinedValues, nameArrays);
const joinedRows = rows.map(function(row) {
return row.join(",");
});
const csvString = joinedRows.join("\n");
postMessage(csvString);
};