|
164 | 164 | return null; |
165 | 165 | } |
166 | 166 |
|
167 | | - if (this.dataInColumns) { |
168 | | - // Transpose the array with data (https://stackoverflow.com/questions/17428587/transposing-a-2d-array-in-javascript) |
169 | | - rawValues = rawValues[0].map((_, colIndex) => rawValues.map(row => row[colIndex])); |
170 | | - } |
| 167 | + // Search for the beginning of data. |
| 168 | + let startRow = 0; |
| 169 | + let startColumn; |
| 170 | + for (const row of rawValues) { |
| 171 | + startColumn = row.findIndex(cell => cell?.effectiveValue); |
171 | 172 |
|
172 | | - // Range of indices of columns with data |
173 | | - const start = rawValues[0].findIndex(el => el?.effectiveValue); |
174 | | - if (start === -1) { |
175 | | - // There is no data to work with |
176 | | - return null; |
| 173 | + if (startColumn !== -1) { |
| 174 | + break; |
| 175 | + } |
| 176 | + |
| 177 | + startRow += 1; |
177 | 178 | } |
178 | 179 |
|
179 | | - const reversed = [...rawValues[0]].reverse(); |
180 | | - const end = reversed.length - reversed.findIndex(el => el?.effectiveValue) - 1; |
| 180 | + if (startRow >= rawValues.length || startColumn === -1) { |
| 181 | + // No data to work with |
| 182 | + return null |
| 183 | + } |
181 | 184 |
|
182 | | - const values = []; |
183 | | - for (let rowIndex = 0; rowIndex < rawValues.length; rowIndex++) { |
184 | | - const row = rawValues[rowIndex]; |
185 | | - if (!row) { |
| 185 | + // Search for the end of the first range of data. |
| 186 | + let endRow; |
| 187 | + for (let row = startRow; row < rawValues.length; row++) { |
| 188 | + if (!rawValues[row]?.[startColumn] || !rawValues[row]?.[startColumn]?.effectiveValue) { |
| 189 | + endRow = row - 1; |
186 | 190 | break; |
187 | 191 | } |
| 192 | + } |
| 193 | + endRow = endRow ?? rawValues.length - 1; |
| 194 | + |
| 195 | + let endColumn = rawValues[startRow].findIndex((cell, index) => index > startColumn && !cell?.effectiveValue); |
| 196 | + endColumn = endColumn === -1 ? rawValues[startRow].length - 1 : endColumn - 1; |
188 | 197 |
|
189 | | - let emptyCellsCount = 0; |
| 198 | + // Save data offset inside raw values to be able to store data back in the same range as the source data. |
| 199 | + this.rowOffset = startRow; |
| 200 | + this.columnOffset = startColumn; |
190 | 201 |
|
| 202 | + let values = []; |
| 203 | + for (let rowIndex = startRow; rowIndex <= endRow; rowIndex++) { |
| 204 | + const row = rawValues[rowIndex]; |
191 | 205 | const ret = []; |
192 | 206 |
|
193 | | - for (let colIndex = start; colIndex <= end; colIndex++) { |
194 | | - const cell = row[colIndex]; |
| 207 | + for (let columnIndex = startColumn; columnIndex <= endColumn; columnIndex++) { |
| 208 | + const cell = row[columnIndex]; |
195 | 209 | let value; |
196 | 210 |
|
197 | 211 | if (!cell?.effectiveValue) { |
198 | 212 | // We have an empty cell |
199 | | - emptyCellsCount += 1; |
200 | 213 | ret.push(undefined); |
201 | 214 | continue; |
202 | 215 | } |
|
237 | 250 | ret.push(value); |
238 | 251 | } |
239 | 252 |
|
240 | | - if (emptyCellsCount === end - start + 1) { |
241 | | - // Skip rows of empty cells |
242 | | - continue; |
243 | | - } |
244 | | - |
245 | 253 | values.push(ret); |
246 | 254 | } |
247 | 255 |
|
| 256 | + if (this.dataInColumns) { |
| 257 | + // Transpose the array with data (https://stackoverflow.com/questions/17428587/transposing-a-2d-array-in-javascript) |
| 258 | + values = values[0].map((_, columnIndex) => values.map(row => row[columnIndex])); |
| 259 | + } |
| 260 | + |
248 | 261 | // We need to store the loaded data so that we can perform diff later. |
249 | 262 | // Why? Because Google Sheets has a built-in version history and we want to benefit from it. |
250 | 263 | // And if every time we overwrite the full data range, it makes the version history useless. |
|
383 | 396 | data = data.concat(records); |
384 | 397 | } |
385 | 398 |
|
| 399 | + // Add “empty” rows and columns to data so we could store them in the same range we got the source data from. |
| 400 | + const emptyRecord = this.columnOffset > 0 ? Array(this.columnOffset + data[0].length).fill(undefined) : []; // [undefined, ..., undefined] |
| 401 | + const emptyRecords = this.rowOffset > 0 ? Array(this.rowOffset).fill(emptyRecord) : []; // [ [undefined, ..., undefined], [undefined, ..., undefined], ..., [undefined, ..., undefined] ] |
| 402 | + |
| 403 | + if (this.columnOffset > 0) { |
| 404 | + // Prepend every row with “empty” columns. |
| 405 | + data = data.map(row => [...Array(this.columnOffset).fill(undefined), ...row]); // [undefined, ..., undefined, data, data, ..., data] |
| 406 | + } |
| 407 | + |
| 408 | + // Prepend data with “empty” rows. |
| 409 | + data = [...emptyRecords, ...data]; // [ [undefined, ..., undefined, undefined, undefined, ..., undefined], ..., [undefined, ..., undefined, undefined, undefined, ..., undefined], [undefined, ..., undefined, data, data, ..., data], ..., [undefined, ..., undefined, data, data, ..., data] ] |
| 410 | + |
386 | 411 | // Write the new data. |
387 | 412 | const url = _.buildURL(this.apiURL, { |
388 | 413 | "valueInputOption": "user_entered", |
|
481 | 506 | }; |
482 | 507 |
|
483 | 508 | // Saved successfully, update the fields. |
484 | | - this.loadedData = res.updatedData.values; |
| 509 | + this.loadedData = res.updatedData?.values; |
485 | 510 | this.recordCount = recordCount; |
486 | 511 |
|
487 | 512 | return res; |
|
0 commit comments