How can I exclude null elements from data array? - javascript

I have been using Yuri's answer to a question, but found myself in a new situation and I haven't quite understood each step of this method here:
function myFunction() {
var sheet = SpreadsheetApp.getActiveSheet();
var header = sheet.getDataRange().getValues()[0];
var data = [
["ID","Variação","Nome Site","Obs"],
[11602,185,"Camisa Teste","Teste da Observação"]
]
var indexes = header.map(x => data[0].indexOf(x));
data = data.map(x => indexes.map(i => x.slice()[0] = x[i]));
sheet.getRange(1, 1, data.length, data[0].length).setValues(data);
}
Here's what the result looks like:
let result = [
["ID","Variação","Nome Site","Obs", "", ""],
[11602,185,"Camisa Teste","Teste da Observação", "", ""]
]
Now, this is returning data with nulls. How can I make it return only mapped array elements?
So, instead of heaving an array length of 6, that'd be only 4.
Expected Result:
let expectedResult = [
["ID","Variação","Nome Site","Obs"],
[11602,185,"Camisa Teste","Teste da Observação"]
]
Thank you!

One possible reason why you are having null elements in your array is that the cells next to your header have " " or space character in it. Since you are using getDataRange() it will also include those " " characters in your array.
Example:
Here, I added space character to cells E1 and F1:
Here are the values of data after using map function:
If I remove the values of cells E1 and F1 the output of data is:
Null values can also occur if the values below the header overlap the number of columns in your header.
Example:
Output:
Solution:
Instead of using getDataRange(), explicitly define the range you are using for the header. You can use either getRange(row, column, numRows, numColumns) or getRange(a1Notation).
Reference:
getRange(a1Notation)
getRange(row, column, numRows, numColumns)

One way is to filter the inner values:
let result = [
["ID", "Variação", "Nome Site", "Obs", "", ""],
[11602, 185, "Camisa Teste", "Teste da Observação", "", ""]
]
result = result.map(x => x.filter(y => y))
console.log(result)

Remove nulls
function test() {
Logger.log(JSON.stringify([1,2,3,null,4,null,5,''].filter(e => e)))
}
Execution log
7:48:37 PM Notice Execution started
7:48:37 PM Info [1,2,3,4,5]
7:48:38 PM Notice Execution completed

Related

Split 2D Array into multiple connected pieces by given rows and columns

I'm trying to find the most efficient way of breaking a two-dimensional array into different two-dimensional pieces, all stored into a new array which will be returned as the result.
I have a 2D array consisting of a string "N M" in the cell at row N and column M. For example, the value in row 2, column 3 would be "2 3".
Now given some rows and column indexes, I want to remove those rows and columns, and group each "piece" into its own 2D array. A "piece" is a set of orthogonally connected cells, without any intervening cells from the rows and columns removed above.
e.g. if I have an array with 8 rows and 7 columns, and I try to remove the rows [2, 3, 5], and columns [2, 6], the resulting array of "pieces" should be
[
[["0, 0", "0, 1"], ["1, 0", "1, 1"]],
[["0, 3", "0, 4", "0, 5"], ["1, 3", "1, 4", "1, 5"]],
[["4, 0", "4, 1"]],
[["4, 3", "4, 4", "4, 5"]],
[["6, 0", "6, 1"], ["7, 0", "7, 1"]],
[["6, 3", "6, 4", "6, 5"], ["7, 3", "7, 4", "7, 5"]]
]
In case the description is unclear, I went ahead and made a visual example of the process in mind here to detail the steps, problems and goals involved.
This is what I was able to come up with, but it's producing arrays that include exception columns. I know why, but I don't know how to get around it short of adding extra complexity which is not conducive to efficiency.
const SourceArray = [ ['R0·C0', 'R0·C1', 'R0·C2', 'R0·C3', 'R0·C4', 'R0·C5', 'R0·C6'],
['R1·C0', 'R1·C1', 'R1·C2', 'R1·C3', 'R1·C4', 'R1·C5', 'R1·C6'],
['R2·C0', 'R2·C1', 'R2·C2', 'R2·C3', 'R2·C4', 'R2·C5', 'R2·C6'],
['R3·C0', 'R3·C1', 'R3·C2', 'R3·C3', 'R3·C4', 'R3·C5', 'R3·C6'],
['R4·C0', 'R4·C1', 'R4·C2', 'R4·C3', 'R4·C4', 'R4·C5', 'R4·C6'],
['R5·C0', 'R5·C1', 'R5·C2', 'R5·C3', 'R5·C4', 'R5·C5', 'R5·C6'],
['R6·C0', 'R6·C1', 'R6·C2', 'R6·C3', 'R6·C4', 'R6·C5', 'R6·C6'],
['R7·C0', 'R7·C1', 'R7·C2', 'R7·C3', 'R7·C4', 'R7·C5', 'R7·C6'] ];
function split2DArray_test() {
let arrObjs = {
oldArr: {
obj: SourceArray,
bounds: { row: 8, col: 7, },
setBounds: function() {
this.bounds.row = this.obj.length;
this.bounds.col = this.obj[0].length;
},
},
newArr: { collection: [], component: [], design: { rInit: 0, rEnd: 0, cInit: 0, cEnd: 0 } },
splits: { atRows: [2, 3, 5], atCols: [2, 5] }
};
arrObjs.oldArr.setBounds();
let i = { lv1_R: 0, lv2_C: 0 , lv3_R: 0, lv4_C: 0, slicer: 0 }; let escape = false;
/*
1. Iterate through rows. (1st level)
2. If row excepted, continue iterating through rows. If not excepted, iterate through columns. (2nd level)
3. If column not excepted, RECORD ADDRESS and continue iterating through column.
4. When excepted column found, RECORD COLUMN - 1, and regin iterating through rows again (3rd level). When excepted row found, RECORD ROW - 1.
5. Record data (SliceArr): [RECORD ADDRESS[R] = RowA, RECORD ADDRESS[C] = ColA, RECORD ROW - 1 = RowB, RECORD COLUMN - 1 = ColB]
6. Create NewArr[] and SliceArrObj[]
7. Iterate through rows (r) from new SliceArr (4th level), and do SliceArrObj.push(OldArr[r].slice(ColA, ColB - ColA))
8. NewArr.push(SliceArrObj)
9. Set 2nd-level iterator to RECORD COLUMN + 1
10. Loop should work theoretically. Untested.
*/
Logger.log(`Excepting rows: ${arrObjs.splits.atRows.join(', ')}`);
Logger.log(`Excepting columns: ${arrObjs.splits.atCols.join(', ')}`);
console.time('slicer');
for (i.lv1_R = 0; i.lv1_R < arrObjs.oldArr.bounds.row; i.lv1_R++) {
if (arrObjs.splits.atRows.includes(i.lv1_R)) { continue; } else {
// We've arrived at a non-excepted row.
for (i.lv2_C = 0; i.lv2_C < arrObjs.oldArr.bounds.col; i.lv2_C++) {
if (arrObjs.splits.atCols.includes(i.lv2_C)) { continue; } else {
arrObjs.newArr.design.rInit = i.lv1_R; arrObjs.newArr.design.cInit = i.lv2_C;
Logger.log(`Found origin cell '${arrObjs.oldArr.obj[i.lv1_R][i.lv2_C]}'`);
for (i.lv3_R = arrObjs.newArr.design.rInit; i.lv3_R < arrObjs.oldArr.bounds.row; i.lv3_R++) {
if (arrObjs.splits.atRows.includes(i.lv3_R)) {
arrObjs.newArr.design.rEnd = i.lv3_R - 1;
for (i.lv4_C = arrObjs.newArr.design.cInit; i.lv4_C < arrObjs.oldArr.bounds.col; i.lv4_C++) {
if (arrObjs.splits.atCols.includes(i.lv4_C)) {
arrObjs.newArr.design.cEnd = i.lv4_C - 1;
for (i.slicer = arrObjs.newArr.design.rInit; i.slicer < arrObjs.newArr.design.rEnd + 1; i.slicer++) {
arrObjs.newArr.component.push([arrObjs.oldArr.obj[i.slicer].slice(arrObjs.newArr.design.cInit, arrObjs.newArr.design.cEnd + 1)]);
};
arrObjs.newArr.collection.push('Split'); // For the sake of output logging.
arrObjs.newArr.collection.push(arrObjs.newArr.component);
arrObjs.newArr.component = [];
i.lv2_C += 1 + arrObjs.newArr.design.cEnd - arrObjs.newArr.design.cInit;
arrObjs.newArr.design.rInit = 0; arrObjs.newArr.design.rEnd = 0; arrObjs.newArr.design.cInit = 0; arrObjs.newArr.design.cEnd = 0;
escape = true; break;
};
};
};
if (escape) { escape = false; break; };
};
};
};
i.lv2_R += 1 + arrObjs.newArr.design.rEnd - arrObjs.newArr.design.rInit;
};
};
console.timeEnd('slicer');
Logger.log(`Splitting 2D Array into:\n\n${(arrObjs.newArr.collection.join('\n\n'))}`); // The output logging.
/* Idea Space:
===== PROPOSED METHOD, INCOMPLETE =====
1. Save the origin cell of the first valid slice bounds.
2. Iterate (FOR) through columns, starting at the Bound Minimum, and going to the Bound Maximum.
3. If an exception row is found, save slice col bound max as (i.c - 1).
4. Now, iterate through rows from slice origin to next exception or bound. Once this is met, save slice row bound max as (i.r - 1).
5. Iterate through slice row bounds, pushing slices with slice column bounds as Array.slice() arguments.
6. Push new 2D array to new array collection.
=== CONCEPTS WORTH TESTING ===
I. Iterative Cell Search
Consider moving forward by setting exception or bound limits. Ex: Column iteration runs into a bound limit: slices.resume.c = false. If it's an exception, slices.resume.c = true. This way,
any time a full 2D slice is pushed to the collection of arrays, it can take the existing current slice data and either iterate across to the next horizontal array (Resume = true), or
iterate down to the next vertical array (Resume = false). If both Column and Row resume are false, then we are done adding sliced 2D arrays to the array collection.
If at least one Resume is true, then another resume can be made true. Once both are true, then a new slice origin point can be made.
II. Iterative Bounds & Exception Search.
1. Table Bounds defined and adjacent exceptions trimmed (Done).
> The current exceptions are now the top-left-side borders of the desired slices.
2. Iterate through exception indices column first (On first loop, operate from top-left bound), then row. On each, set slice start to (i.r + 1, i.c + 1), where i.? = the exception column or
row. Set the end bounds to the index represented by the next element in the list (column or row).
*/
};
In the approach I take below, I split up the problem in two main steps:
const ranges = (splits, length) => [...splits, length].reduce(
(r, e, i, a) => (s = i ? a[i - 1] + 1 : 0, e - s ? [...r, {s, e}] : r),
[]
);
const blocks = (array, rowRanges, colRanges) => rowRanges.map(
({s, e}) => array.slice(s, e)
).map(block => colRanges.map(
({s, e}) => block.map(row => row.slice(s,e))
)).flat();
The ranges() function takes an input array containing "splits" information (i.e. rowSplits or colSplits), and then uses that information to create an array of ranges for the row blocks or column blocks. Each range is defined as an object of type {s: number, e: number}, where s is the start of the range (inclusive) and e is the end (exclusive). The reduce operation used in this function makes it so that empty ranges are filtered out.
The blocks() function takes the 2D array and the row and column ranges as input. It first uses the row ranges to split the 2D array into row blocks, and then uses the column ranges to split those blocks down to the regions defined in the problem statement. The splitting operations are all implemented by mapping the ranges to their corresponding array slices. Finally, the entire result is flattened to arrive at the required output.
Complete snippet:
const rows = 8;
const cols = 7;
const arrOld = Array.from(
{ length: rows}, (_, y) => Array.from(
{ length: cols }, (_, x) => `${y}, ${x}`
)
);
const rowSplits = [2, 3, 5];
const colSplits = [2, 6];
const ranges = (splits, length) => [...splits, length].reduce(
(r, e, i, a) => (s = i ? a[i - 1] + 1 : 0, e - s ? [...r, {s, e}] : r),
[]
);
const blocks = (array, rowRanges, colRanges) => rowRanges.map(
({s, e}) => array.slice(s, e)
).map(block => colRanges.map(
({s, e}) => block.map(row => row.slice(s,e))
)).flat();
const arrNew = blocks(
arrOld,
ranges(rowSplits, rows),
ranges(colSplits, cols)
);
console.log(arrNew);

Retrieving a full row of google sheets data to JavaScript

I am trying to build a form that can recall data based on a specific number being inputted using Google Scripts and JavaScript. When a number is inputted the JavaScript should call the Google Scripts so that it can locate the values I need based on the number. For example bellow is a google sheets. I want when the user inputs a number it searches for that value in column C and then grabs the data from that row. Ex. if the number is 14 then values: 2021-05-12, 5, 6 and 7 are returned to the JavaScript.
UPDATED:
Everything I want is working however, when I try to retrieve the date from the array in the JavaScript it does not work. The numbers are the only thing functioning. I know the date is in the array as I can see it in the logs.
JavaScript:
function callDataRetriever(){
var number = document.getElementById("number").value;
google.script.run.withSuccessHandler(dataRetriever).retreiveData(number);
}
function dataRetriever(data){
document.getElementById("location").value = data[0]; //This works
document.getElementById("dateOpened").value = data[1]; //This does not work. Stops the function from continuing its task.
document.getElementById("value1").value = data[2]; //Without the date everything here down works
document.getElementById("value2").value = data[3];
document.getElementById("value2").value = data[4];
document.getElementById("value4").value = data[5];
//...
}
Google Scripts (I have 28 total values) Everything here works perfectly as seen in the logs bellow.
function retreiveData(number){
var url = "urlToSpreadsheet";
var ss = SpreadsheetApp.openByUrl(url);
var ws = ss.getSheetByName("Data");
var data = ws.getRange(1,1, ws.getLastRow(), ws.getLastColumn()).getValues();
var dataValues = [];
var filterData = data.filter(
function(r){
if(r[2] == number){
var i = 3;
while(i < 29){
dataValues.push(r[i]);
i++;
}
}
}
)
return dataValues;
}
I am not sure if the problem lies in the way the date is formatted.
Thank you!
You can use filter to perform the operation you want
var filterData = data.filter(
function(r){
//Select the index init in 0 in your case is 2
return r[0] == 'YOUR_SEARCH_VALUE'
}
)
// Use your filter data
ui.alert(filterData[0]) //data[row][column]
I believe your goal as follows.
When number is given and run the function retreiveData, you want to search the value of number from the cells "C4:C" of "Data" sheet, and want to retrieve the values of the columns "D", "E" and "F" for the searched row.
In this case, I would like to propose to use TextFinder. When the TextFinder is used, searching value is run in the internal server side. By this, the process cost will be a bit low. Ref
When this is reflected to your script, it becomes as follows.
Modified script:
In this case, please modify retreiveData at Google Apps Script side as follows.
function retreiveData(number){
var url = "linkToSpreadsheet";
var ss = SpreadsheetApp.openByUrl(url);
var ws = ss.getSheetByName("Data");
var data = ws.getRange("C4:C" + ws.getLastRow()).createTextFinder(number).matchEntireCell(true).findNext();
if (data) {
return data.offset(0, 1, 1, 4).getValues()[0];
}
return Array(4).fill("No value");
}
And, please modify dataRetriever at Javascript side as follows.
function dataRetriever(data){
console.log(data) // Here, you can check the value from `retreiveData` at the console.
data.forEach((v, i) => {
document.getElementById(`value${i + 1}`).value = v;
});
}
Note:
In this case, when the search value of number is not found, No value is put to the input tags of value1 to value4. When you want to modify this, please modify return Array(4).fill("No value"); for your actual situation.
References:
Benchmark: Process Costs for Searching Values in Spreadsheet using Google Apps Script
Class TextFinder
There are two ways:
var data = [
[1, 55, 5545, 54, 51],
[2, 45, 541, 848, 1215],
[3, 323, 3232, 215, 3051],
[4, 13, 5151, 1513, 2315]
]
number = 3;
// via object where the 'numbers' are keys
var data_obj = Object.fromEntries(data.map(d => [d[0], d.slice(1,)]));
console.log(data_obj[number]); // [ 323, 3232, 215, 3051 ]
// via filter (about the same as #macorreag's answer)
var res = data.filter(d => d[0] == number)[0].slice(1,);
console.log(res); // [ 323, 3232, 215, 3051 ]
An object makes sense in the cases when you need to extract info from the same data several times. For single requests a filter looks better, I think.
And if the numbers always start from 1 and go sequentially 2, 3, 4, 5, ... etc, the simpliest solution is an array and its indexes:
var data = [
[1, 55, 5545, 54, 51],
[2, 45, 541, 848, 1215],
[3, 323, 3232, 215, 3051],
[4, 13, 5151, 1513, 2315]
]
var number = 3;
var data_array = data.map(d => d.slice(1,));
console.log(data_array[number-1]); // [ 323, 3232, 215, 3051 ]
In this case you don't even need the first column 'numbers' at all. You can just extract the next four columns and get the rows by indexes (-1): number 1 is data[0], number 2 is data[1] etc.

Printing Unspecified Number of Array Contents in Google Sheets Cells (GAS)

I am collecting information from a database, based on a filter and is stored in an array of arrays. Once this information is collected I want to be able to print this data onto a new sheet like this:
___________________________________
|*****|John Buck, Associate, Luxura|
------------------------------------
|*****|Jane Doe, Manager, Eclipse |
------------------------------------
|*****|Jim Bob, Executive, Lokia |
------------------------------------
|*****|Suzzy Sue, Director, RoomX |
___________________________________
The most important part is that each time I run this function the array may be a different size, so I need it to continue through the array until all records have been printed down the sheet. I've tried a few things, but I am only used to using forEach or for loops when there is a condition, where as this one would just print the specified info in every array.
function testArray() {
var activeSpreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var activeSheet = activeSpreadsheet.getActiveSheet();
var activeRange = activeSpreadsheet.getActiveRange();
var activeCell = activeSpreadsheet.getActiveCell();
var startCell = activeSheet.getRange(5, 4);
var ratingStartCell = activeSheet.getRange(5, 3);
var newArray = [
["John Buck", "*****", "Associate", "Luxura"][
("Jane Doe", "****", "Manager", "Eclipse")
][("Jim Bob", "***", "Executive", "lokia")][
("Suzzy Sue", "*****", "Director", "RoomX")
],
];
newArray.forEach((r, o) => {
startCell.setValues(
newArray[r][0] +
", " +
newArray[r][2] +
", " +
newArray[r][3] +
", " +
newArray[r][4]
);
});
newArray.forEach((r, o) => {
ratingStartCell.setValues(newArray[r][2]);
});
}
By the way, I modified your array. I'm not sure why it contains "()", you can refer to JavaScript Array to learn more on how to declare and use arrays.
You can refer to this sample code:
function testArray() {
var activeSpreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var activeSheet = activeSpreadsheet.getActiveSheet();
Logger.log(activeSheet.getSheetName());
var newArray = [
["John Buck", "*****", "Associate", "Luxura"],
["Jane Doe", "****", "Manager", "Eclipse"],
["Jim Bob", "***", "Executive", "lokia"],
["Suzzy Sue", "*****", "Director", "RoomX"]
];
Logger.log(newArray);
//Create new array that will contain expected row values
var rowValues = [];
newArray.forEach((r,o)=>{
//Push a 2-D array, Col1 will be the rating (****) while Col2 will be the remaining strings
//separated by a comma and space
rowValues.push([r[1], r[0]+", "+r[2]+", "+r[3]]);
});
Logger.log(rowValues);
//Set the values for the selected range, using the size of the 2-d array starting from row 5.
activeSheet.getRange(5,3,rowValues.length,rowValues[0].length).setValues(rowValues);
}
What it does?
After fixing your array, I created a new array rowValues which will contain 2 information:
Rating
Concatenated strings separated by a comma and a space.
Once I have created the desired grouping and output for each row. I now set the values of the selected range based on the rowValues created using Sheet.getRange(row, column, numRows, numColumns) and Range.setValues(values)
I set the start row to 5 and start column to 3 based on your earlier example. Feel free to adjust your start row and column index based on your preference.
OUTPUT:
You could post the newArray to activeSheet in one go using the setValues(values) method.
This is more efficient than writing to the sheet on cell at a time. See Best Practices.
activeSheet.getRange(1, 1, newArray.length, newArray[0].length).setValues(newArray);
The size of the array does not need to be specified. newArray.length is the number of rows and newArray[0].length is the number of columns.
You need to be careful when constructing the array: each row must have the same number of columns. If the number of columns is different, you'll get an error and the setValues() will fail.

Why does function A work and function B throw an error?

I have 2 functions, one works and the other does not - the reason the second function is more important is that essentially I want to be able to change what's in the final array. Deconstruction is great and all but I don't expect to have the same incoming data landing in the same place - elsewhere I fixed this issue by creating individual arrays and just having all my functions return data by index, but I'm a little bamboozled here.
Heres the first one (createLogs) that does what I want it to do and if the data always came, in the same way, would do more than enough.
function createLogs(array,runtimeArray) {
let basicsLog=[]
array.map(function callback(currentValue,index) {
let currentRuntime = runtimeArray[index]
let [,date,time,systemMode,systemSetting,calEvent,ProgramMode,coolSetTemp,heatSetTemp,currentTemp] = currentValue
basicsLog.push([index,date,time,systemMode,systemSetting,calEvent,ProgramMode,coolSetTemp,heatSetTemp,currentTemp,currentRuntime])
})
console.log(basicsLog)
return basicsLog
}
Returns
[ 36,
2018-11-19T05:00:00.000Z,
'second failure',
'heat',
'heatOff',
'hold',
'Sleep',
70,
70,
69.5,
135 ],
[ 37,
2018-11-19T05:00:00.000Z,
'2:35:00',
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
null ] ]
Now here is createResults() - I think much better for a situation where what comes in is highly variable (basically its a table , and the columns in the table are different in different situations)
createResults() looks like this:
function createResults(originalThermostatArray,dateArray,timeArray, systemSettingsArray, systemModeArray, calendarEventArray, programModeArray, coolSetTempArray, heatSetTempArray, currentTempArray, thermostatTempArray, outdoorTempArray, combinedRuntimesArray) {
let resultArray = []
originalThermostatArray.forEach(function(currentValue,index) {
let currentDate = dateArray[index]
let currentTime = timeArray[index]
let currentSystemSetting = systemSettingsArray[index]
let currentSystemMode = systemModeArray[index]
let currentCalEvent = calendarEventArray[index]
let currentProgramMode = programModeArray[index]
let currentCoolSetTemp = coolSetTempArray[index]
let currentHeatSetTemp = heatSetTempArray[index]
let currentAvgTemp = currentTempArray[index]
let currentThermostatTemp = thermostatTempArray[index]
let currentOutdoorTemp = outdoorTempArray[index]
let currentRuntime = combinedRuntimesArray[index]
resultArray.push([index, currentDate, currentTime, currentSystemSetting, currentSystemMode, currentCalEvent,currentProgramMode, currentCoolSetTemp, currentHeatSetTemp, currentAvgTemp, currentThermostatTemp, currentOutdoorTemp, currentRuntime])
})
console.log(resultArray)
return resultArray
}
And Returns
[ 36,
2018-11-19T05:00:00.000Z,
'second failure',
'heat',
'heatOff',
'hold',
'Sleep',
70,
70,
69.5,
69.5,
23.5,
135 ],
[ 37,
2018-11-19T05:00:00.000Z,
'2:35:00',
null,
null,
null,
null,
null,
null,
null,
null,
23.5,
null ] ]
On the front end the code looks like this
div.resultSummaries.equipmentSummary
h1.summaryHeader.equipmentSummaryHead Possible Equipment Failures
h3.equipmentSummaryBody #{equipresultCount}
p.equipmentSummaryBody This table looks for increasing thermostat temperatures during a cooling call.
table.fullResults.equipmentResultsTable
caption.equipTitle Possible Equipment Failures Log
thead
tr
th.resultHeader.equipmentHeader Index
th.resultHeader.equipmentHeader Date
th.resultHeader.equipmentHeader Time
th.resultHeader.equipmentHeader System Setting
th.resultHeader.equipmentHeader System Mode
th.resultHeader.equipmentHeader Calendar Event
th.resultHeader.equipmentHeader Program Mode
th.resultHeader.equipmentHeader Cool Set Temp
th.resultHeader.equipmentHeader Heat Set Temp
th.resultHeader.equipmentHeader Avg Temp
th.resultHeader.equipmentHeader Thermostat Temp
th.resultHeader.equipmentHeader Outdoor Temp
th.resultHeader.equipmentHeader Runtime(sec)
tr
tbody
- let equipFailsResultLog = result.equipLogs;
for item in equipFailsResultLog
tr.equipRow
for item in equipArray
td.resultTable.result.equipResult #{item}
tfoot
But createResults() returns this error on the front end:
/views/upload.pug:137 135| for item in equipFailsResultLog 136| tr.equipRow > 137| for item in equipArray 138| td.resultTable.result.equipResult #{item} 139| tfoot 140| Cannot read property 'length' of undefined.
I think the fact that they return differently is causing the error but why? note that I tried writing createResults() as a for each and a map
function1 (createlogs) allows me to get into the array and access the individual elements and place them correctly
function2 (createResults) does not..
How can I access the nested elements using function 2 or display them the way I would want to?
added:
create results is called like this
const basicLogsArray = returnLogic.createResult(originalThermostatArray,dateArray,timeArray,systemSettingsArray,systemModeArray,calendarEventArray,programModeArray,coolSetTempArray,heatSetTempArray,currentTempArray,thermostatTempArray,outdoorTempArray,combinedRuntimesArray)
create log is called similarly but with just array and runtime array.
const equipFailsLogs = reportLogic.rowsByIndex(
basicLogsArray,
possibleEquipmentFailuresArray
)
console.log(equipFailsLogs)
This is what goes to the front end. (i run another function to get the whole row each bit of date is associated with and return it as an html table in pug.
return {
"Results": {
"coolCount":coolFailsRows.length/2 , coolLogs:coolFailsLogs,
"heatCount":heatFailsRows.length/2 , heatLogs:heatFailsLogs,
"miscCount":miscFailsRows.length/2 , miscLogs:miscfailsLogs,
"equipCount":equipFailsLogs.length/4 , equipLogs:equipFailsLogs,
}};
}
exports.runThermostat = runThermostat

Read CSV file based on columns

I am working in an Angular js project where I have to read the CSV file column wise using javascript. I have retrieved the CSV and printed it in the console. I am able to split the csv row wise. How to do it based on columns?
Thanks in advance.
CSV is nothing but a 2 dim array. By using 2 for loop you can do.
for example:
for(var i=0;i<col.length;i++){
for(var j=0;j<rows.length;j++){
arr[i][j]
}
}
Interestingly, it sounds easy to parse CSV files but it can get complicated quickly (depending on where your CSV comes from: user-generated, fixed in format from an API, ...):
Parsing headers
Different number of headers and data columns
Delimiter (Germans often use ; instead of ,)
Number format (Germans often use the , instead of . as decimal separators)
Quoted data (maybe with a delimiter inside the quote data)
Whitespace
Line endings
...
That's why there are a lot of CSV parsers available on npm (https://www.npmjs.com/search?q=csv). Some are focused on parsing speed (e.g. https://www.npmjs.com/package/fast-csv) some on convenience (e.g. https://www.npmjs.com/package/papaparse).
But all have in common (as far as I know) that they return row wise. Which is often caused to be able to process streams which will pass the data line wise and not column wise.
Here's a code example to get your data column wise:
const input = `header_1,header_2
value 1,value 2
value 3,value 4`
// get rows -> here \n as line ending is known
const rows = input.split('\n');
// get the first row as header
const header = rows.shift();
// get number of columns by splitting the header
// , as delimiter is known here. Could be different ...
const numberOfColumns = header.split(',').length
// initialize 2D-array with a fixed size
const columnData = [...Array(numberOfColumns)].map(item => new Array());
for(var i=0; i<rows.length; i++) {
var row = rows[i];
var rowData = row.split(',');
// assuming that there's always the same
// number of columns in data rows as in header row
for(var j=0; j<numberOfColumns; j++) {
columnData[j].push(rowData[j]);
}
}
console.log("columnData = " + JSON.stringify(columnData, null, 4));
Output will be:
columnData = [
[
"value 1",
"value 3"
],
[
"value 2",
"value 4"
]
]
Not included is stripping whitespace, converting numbers, ...
For convinience you could use papaparse to get the data row wise and use the nested for loop again:
const Papa = require('papaparse');
// example data with delimiter ; and empty lines
const input = `header_1;header_2
1;"2"
3;4`;
// options for papaparse
const parseOptions = {
quoteChar: '"', // quoting character
delimiter: ';',
skipEmptyLines: true, // ignore empty lines
dynamicTyping: true, // parse numbers automatically
}
const parseResult = Papa.parse(input, parseOptions);
const parsedData = parseResult.data;
// get the first row as header
const header = parsedData.shift();
const numberOfColumns = header.length;
// initialize 2D-array with a fixed size
const columnData = [...Array(numberOfColumns)].map(item => new Array());
for(var i=0; i<parsedData.length; i++) {
var rowData = parsedData[i];
for(var j=0; j<numberOfColumns; j++) {
columnData[j].push(rowData[j]);
}
}
console.log("columnData = " + JSON.stringify(columnData, null, 4));
Output will be:
columnData = [
[
1,
3
],
[
2,
4
]
]

Categories

Resources