{"id":17381758,"url":"https://github.com/tanaikech/UtlApp","last_synced_at":"2025-02-27T10:30:29.264Z","repository":{"id":164708732,"uuid":"640153163","full_name":"tanaikech/UtlApp","owner":"tanaikech","description":"This is a Google Apps Script library including useful scripts for supporting to development of applications by Google Apps Script.","archived":false,"fork":false,"pushed_at":"2024-05-04T11:04:18.000Z","size":563,"stargazers_count":12,"open_issues_count":0,"forks_count":2,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-05-04T12:22:28.389Z","etag":null,"topics":["developer-tool","google-apps-script","google-apps-script-library","javascript","library"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tanaikech.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-05-13T06:45:52.000Z","updated_at":"2024-05-04T12:22:41.169Z","dependencies_parsed_at":"2023-11-12T02:30:56.127Z","dependency_job_id":"823a2eea-7495-47d2-bcb5-31eb052a507f","html_url":"https://github.com/tanaikech/UtlApp","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tanaikech%2FUtlApp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tanaikech%2FUtlApp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tanaikech%2FUtlApp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tanaikech%2FUtlApp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tanaikech","download_url":"https://codeload.github.com/tanaikech/UtlApp/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":219842819,"owners_count":16556564,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["developer-tool","google-apps-script","google-apps-script-library","javascript","library"],"created_at":"2024-10-16T07:01:37.083Z","updated_at":"2025-02-27T10:30:29.253Z","avatar_url":"https://github.com/tanaikech.png","language":"JavaScript","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"readme":"# UtlApp\n\n\u003ca name=\"top\"\u003e\u003c/a\u003e\n[MIT License](LICENCE)\n\n\u003ca name=\"overview\"\u003e\u003c/a\u003e\n\n# Overview\n\nThis is a Google Apps Script library including useful scripts for supporting to development of applications by Google Apps Script. In the current stage, the 3 categories \"For array processing\", \"For binary processing\", and \"For string processing\" are included in this library.\n\n![](images/fig1.png)\n\n\u003ca name=\"description\"\u003e\u003c/a\u003e\n\n# Description\n\nWhen I create applications using Google Apps Script, there are useful scripts for often use. At that time, I thought that when those scripts can be simply used, they will be useful not only to me but also to other users. From this motivation, I created a Google Apps Script library including those scripts. But, I have been using these useful scripts only in my development before.\n\nI sometimes answer questions related to Google Apps Script at Stackoverflow. From my experience that I have answered those questions, I also added useful scripts, that I believe, in the library. Some of these scripts will be also the direct answer to the questions. If this was useful for your situation, I'm glad.\n\n# Policy of this library\n\n**The policy of this library is not to use any scopes.**\n\n# Library's project key\n\n```\n1idMI9-WtPMbYvbK5D7KH2_GWh62Dny9RG8NzjwjHI5whGIAPXEtTJmeC\n```\n\n\u003ca name=\"usage\"\u003e\u003c/a\u003e\n\n# Usage\n\n## 1. Install library\n\nIn order to use this library, please install the library as follows.\n\n1. Create a GAS project.\n\n   - You can use this library for the GAS project of both the standalone type and the container-bound script type.\n\n1. [Install this library](https://developers.google.com/apps-script/guides/libraries).\n\n   - Library's project key is **`1idMI9-WtPMbYvbK5D7KH2_GWh62Dny9RG8NzjwjHI5whGIAPXEtTJmeC`**.\n\n# Methods\n\n## For array processing\n\n| Methods                                           | Description                                                                     |\n| :------------------------------------------------ | :------------------------------------------------------------------------------ |\n| [is2DimensionalArray](#is2dimensionalarray)       | When the inputted array is 2 dimensional array, true is returned.               |\n| [isUniform2DArray](#isuniform2darray)             | When the inputted 2 dimensional array is the uniformed array, true is returned. |\n| [uniform2DArray](#uniform2darray)                 | Make all array in 2 dimensional array uniforming the same length.               |\n| [transpose](#transpose)                           | Transpose 2 dimensional array.                                                  |\n| [splitArray](#splitarray)                         | Split array every n length.                                                     |\n| [getSpecificColumns](#getspecificcolumns)         | Retrieve the specific columns from 2 dimensional array.                         |\n| [deleteSpecificColumns](#deletespecificcolumns)   | Delete specific columns from 2 dimensional array.                               |\n| [insertColumns](#insertcolumns)                   | Insert columns to 2 dimensional array.                                          |\n| [removeDuplicatedValues](#removeduplicatedvalues) | Remove duplicated values from 1 dimensional array.                              |\n| [get1stEmptyRow](#get1stemptyrow)                 | Retrieve empty row index.                                                       |\n| [get1stEmptyColumn](#get1stemptycolumn)           | Retrieve empty column index.                                                    |\n| [sum](#sum)                                       | Sum numbers in an array.                                                        |\n| [compilingNumbers](#compilingnumbers)             | Compiling Continuous Numbers using Google Apps Script.                          |\n| [convArrayToObject](#convarraytoobject)           | Converting 2 dimensional array to JSON object.                                  |\n| [unpivot](#unpivot)                               | Converting 2-dimensional array as unpivot (reverse pivot).                      |\n| [reverseUnpivot](#reverseunpivot)                 | Reversing 2-dimensional array with unpivot.                                     |\n| [dotProduct](#dotproduct)                         | Calculate dot product from 2 arrays.                                            |\n| [cosineSimilarity](#cosinesimilarity)             | Calculate cosine similarity from 2 arrays.                                      |\n\n## For binary processing\n\n| Methods                                                   | Description                                      |\n| :-------------------------------------------------------- | :----------------------------------------------- |\n| [convInt8ArrayToHexAr](#convint8arraytohexar)             | Convert Int8Array to hex string array.           |\n| [convStrToHex](#convstrtohex)                             | Convert string to hex.                           |\n| [convHexToInt8Ar](#convhextoint8ar)                       | Convert string to hex.                           |\n| [convInt8ArToStr](#convint8artostr)                       | Convert Int8Array to string value.               |\n| [convInt8ArToUint8Ar](#convint8artouint8ar)               | Convert Int8Array to Uint8Array.                 |\n| [convUint8ArToInt8Ar](#convuint8artoint8ar)               | Convert Uint8Array to Int8Array.                 |\n| [searchIndexFromDataByData](#searchindexfromdatabydata)   | Search index from base data using a search data. |\n| [splitByteArrayBySearchData](#splitbytearraybysearchdata) | Split byteArray by a search data.                |\n\n## For string processing\n\n| Methods                                                 | Description                                                                      |\n| :------------------------------------------------------ | :------------------------------------------------------------------------------- |\n| [ConvText](#convtext)                                   | Converting text as unicode.                                                      |\n| [columnLetterToIndex](#columnlettertoindex)             | Converting colum letter to column index. Start of column index is 0.             |\n| [columnIndexToLetter](#columnindextoletter)             | Converting colum index to column letter. Start of column index is 0.             |\n| [convA1NotationToGridRange](#conva1notationtogridrange) | Converting a1Notation to gridrange. This will be useful for using Sheets API.    |\n| [convGridRangeToA1Notation](#convgridrangetoa1notation) | Converting gridrange to a1Notation. This will be useful for using Sheets API.    |\n| [addQueryParameters](#addqueryparameters)               | This method is used for adding the query parameters to the URL.                  |\n| [parseQueryParameters](#parsequeryparameters)           | This method is used for parsing the URL including the query parameters.          |\n| [expandA1Notations](#expandA1Notations)                 | This method is used for expanding A1Notations.                                   |\n| [consolidateA1Notations](#consolidatea1notations)       | This method is used for consolidating the scattered A1Notations.                 |\n| [blobToDataUrl](#blobtodataurl)                         | This method is used for converting Blob to the data URL.                         |\n| [snake_caseToCamelCase](#snakecasetocamelcase)          | This method is used for converting a string of the snake case to the camel case. |\n| [camelCaseTosnake_case](#camelcasetosnakecase)          | This method is used for converting a string of the camel case to the snake case. |\n| [createFormDataObject](#createformdataobject)           | This method is used for creating the form data to HTTP request from an object.   |\n\n## Show document in script editor\n\nWhen you use this library with the script editor of Google Apps Script, you can see the document of each method by the autocompletion of the script editor. You can see the following demonstration.\n\n![](images/fig2.gif)\n\n# Scripts of all methods\n\n---\n\n## For array processing\n\n\u003ca name=\"is2dimensionalarray\"\u003e\u003c/a\u003e\n\n### is2DimensionalArray\n\nWhen the inputted array is 2 dimensional array, true is returned.\n\n````javascript\n/**\n * ### Description\n * When the inputted array is 2 dimensional array, true is returned.\n *\n * ### Sample script\n * ```\n * const array1 = [\"\", [1, 2, 3], [1, 2, 3], [1, 2, 3]];\n * const res1 = UtlApp.is2DimensionalArray(array1); // false\n *\n * const array2 = [[1, 2, 3], [1, 2], [1]];\n * const res2 = UtlApp.is2DimensionalArray(array2); // true\n * ```\n *\n * @param {Array} array 2 dimensional array.\n * @return {Boolean} When the inputted array is 2 dimensional array, true is returned.\n */\nfunction is2DimensionalArray(array) {\n  return array.every((r) =\u003e Array.isArray(r));\n}\n````\n\n\u003ca name=\"isuniform2darray\"\u003e\u003c/a\u003e\n\n### isUniform2DArray\n\nWhen the inputted 2 dimensional array is the uniformed array, true is returned.\n\n````javascript\n/**\n * ### Description\n * When the inputted 2 dimensional array is the uniformed array, true is returned.\n *\n * ### Sample script\n * ```\n * const array1 = [[1, 2, 3], [1, 2, 3], [1, 2, 3]];\n * const res1 = UtlApp.isUniform2DArray(array1); // true\n *\n * const array2 = [[1, 2, 3], [1, 2], [1]];\n * const res2 = UtlApp.isUniform2DArray(array2); // false\n * ```\n *\n * @param {Array} array 2 dimensional array.\n * @param {Boolean} check Check whether the inputted array is 2 dimensional array. Default is true.\n * @return {Boolean} When the inputted 2 dimensional array is the uniformed array, true is returned.\n */\nfunction isUniform2DArray(array, check = true) {\n  if (check \u0026\u0026 !is2DimensionalArray(array)) {\n    throw new Error(\"Please use 2 dimensional array.\");\n  }\n  return new Set(array.map((r) =\u003e r.length)).size == 1 ? true : false;\n}\n````\n\n\u003ca name=\"uniform2darray\"\u003e\u003c/a\u003e\n\n### uniform2DArray\n\nMake all array in 2 dimensional array uniforming the same length.\n\n````javascript\n/**\n * ### Description\n * Make all array in 2 dimensional array uniforming the same length.\n *\n * ### Sample script\n * ```\n * const array1 = [[1, 2, 3], [1, 2, 3], [1, 2, 3]];\n * const res1 = UtlApp.uniform2DArray(array1);\n * console.log(res1); // [ [ 1, 2, 3 ], [ 1, 2, 3 ], [ 1, 2, 3 ] ]\n *\n * const array2 = [[1, 2, 3], [1, 2], [1]];\n * const res2 = UtlApp.uniform2DArray(array2);\n * console.log(res2); // [ [ 1, 2, 3 ], [ 1, 2, null ], [ 1, null, null ] ]\n * ```\n *\n * @param {Array} array 2 dimensional array.\n * @param {*} empty Value used in the added element. Default is null.\n * @param {Boolean} check Check whether the inputted array is 2 dimensional array. Default is true.\n * @return {Array} Uniformed array.\n */\nfunction uniform2DArray(array, empty = null, check = true) {\n  if (check \u0026\u0026 !is2DimensionalArray(array)) {\n    throw new Error(\"Please use 2 dimensional array.\");\n  }\n  const maxLen = Math.max(...array.map((r) =\u003e r.length));\n  return array.map((r) =\u003e [...r, ...Array(maxLen - r.length).fill(empty)]);\n}\n````\n\n\u003ca name=\"transpose\"\u003e\u003c/a\u003e\n\n### transpose\n\nTranspose 2 dimensional array.\n\n````javascript\n/**\n * ### Description\n * Transpose 2 dimensional array.\n *\n * ### Sample script\n * ```\n * const array1 = [[1, 2, 3], [1, 2, 3], [1, 2, 3]];\n * const res1 = UtlApp.transpose(array1);\n * console.log(res1); // [ [ 1, 1, 1 ], [ 2, 2, 2 ], [ 3, 3, 3 ] ]\n *\n * const array2 = [[1, 2, 3], [1, 2], [1]];\n * const res2 = UtlApp.transpose(array2);\n * console.log(res2); // [ [ 1, 1, 1 ], [ 2, 2, null ], [ 3, null, null ] ]\n * ```\n *\n * @param {Array} array 2 dimensional array.\n * @param {Boolean} check Check whether the inputted array is 2 dimensional array. Default is true.\n * @return {Array} Transposed array.\n */\nfunction transpose(array, check = true) {\n  if (check \u0026\u0026 !is2DimensionalArray(array)) {\n    throw new Error(\"Please use 2 dimensional array.\");\n  }\n  return array[0].map((_, col) =\u003e array.map((row) =\u003e row[col] || null));\n}\n````\n\n\u003ca name=\"splitarray\"\u003e\u003c/a\u003e\n\n### splitArray\n\nSplit array every n length. [Ref](https://tanaikech.github.io/2022/05/21/splitting-and-processing-an-array-every-n-length-using-google-apps-script/)\n\n````javascript\n/**\n * ### Description\n * Split array every n length.\n * Ref: https://tanaikech.github.io/2022/05/21/splitting-and-processing-an-array-every-n-length-using-google-apps-script/\n *\n * ### Sample script\n * ```\n * const size = 3;\n * const array1 = [\"a1\", \"b1\", \"c1\", \"d1\", \"e1\", \"f1\", \"g1\", \"h1\", \"i1\", \"j1\"];\n * const res = UtlApp.splitArray(array1, size);\n * console.log(res);\n * ```\n *\n * Result is as follows.\n *\n * ```\n * [\n *   [ 'a1', 'b1', 'c1' ],\n *   [ 'd1', 'e1', 'f1' ],\n *   [ 'g1', 'h1', 'i1' ],\n *   [ 'j1' ]\n * ]\n * ```\n *\n * @param {Array} array 2 dimensional array.\n * @param {Boolean} check Check whether the inputted array is 2 dimensional array. Default is true.\n * @return {Array} Transposed array.\n */\nfunction splitArray(array, size) {\n  if (!array || !size || !Array.isArray(array)) {\n    throw new Error(\"Please give an array and split size.\");\n  }\n  return [...Array(Math.ceil(array.length / size))].map((_) =\u003e\n    array.splice(0, size)\n  );\n}\n````\n\n\u003ca name=\"getspecificcolumns\"\u003e\u003c/a\u003e\n\n### getSpecificColumns\n\nRetrieve the specific columns from 2 dimensional array.\n\n````javascript\n/**\n * ### Description\n * Retrieve the specific columns from 2 dimensional array.\n *\n * ### Sample script\n * ```\n * const array1 = [[1, 2, 3], [1, 2, 3], [1, 2, 3]];\n * const columns = [2];\n * const res = UtlApp.getSpecificColumns(array1, columns);\n * console.log(res); // [ [ 2 ], [ 2 ], [ 2 ] ]\n * ```\n *\n * @param {Array} array 2 dimensional array.\n * @param {Array} columns 1 dimensional array. Give the specific column numbers you want to get. The 1st number is 1.\n * @param {Boolean} check Check whether the inputted array is 2 dimensional array. Default is true.\n * @return {Array} Retrieved specific columns.\n */\nfunction getSpecificColumns(array, columns, check = true) {\n  if (check \u0026\u0026 !is2DimensionalArray(array)) {\n    throw new Error(\"Please use 2 dimensional array.\");\n  }\n  if (!columns || !Array.isArray(columns) || columns.length == 0) {\n    throw new Error(\"Please set column numbers you want to retrieve.\");\n  }\n  return array.map((r) =\u003e columns.map((e) =\u003e r[e - 1] || null));\n}\n````\n\n\u003ca name=\"deletespecificcolumns\"\u003e\u003c/a\u003e\n\n### deleteSpecificColumns\n\nDelete specific columns from 2 dimensional array.\n\n````javascript\n/**\n * ### Description\n * Delete specific columns from 2 dimensional array.\n *\n * ### Sample script\n * ```\n * const array1 = [[1, 2, 3], [1, 2, 3], [1, 2, 3]];\n * const columns = [2];\n * const res = UtlApp.deleteSpecificColumns(array1, columns);\n * console.log(res); // [ [ 1, 3 ], [ 1, 3 ], [ 1, 3 ] ]\n * ```\n *\n * @param {Array} array 2 dimensional array.\n * @param {Array} columns 1 dimensional array. Give the specific column numbers you want to delete. The 1st number is 1.\n * @param {Boolean} check Check whether the inputted array is 2 dimensional array. Default is true.\n * @return {Array} Array deleted the specific columns.\n */\nfunction deleteSpecificColumns(array, columns, check = true) {\n  if (check \u0026\u0026 !is2DimensionalArray(array)) {\n    throw new Error(\"Please use 2 dimensional array.\");\n  }\n  if (!columns || !Array.isArray(columns) || columns.length == 0) {\n    throw new Error(\"Please set column numbers you want to retrieve.\");\n  }\n  return array.map((r) =\u003e r.filter((_, j) =\u003e !columns.includes(j + 1)));\n}\n````\n\n\u003ca name=\"insertcolumns\"\u003e\u003c/a\u003e\n\n### insertColumns\n\nInsert columns to 2 dimensional array.\n\n````javascript\n/**\n * ### Description\n * Insert columns to 2 dimensional array.\n *\n * ### Sample script\n * ```\n * const array1 = [[1, 2, 3], [1, 2, 3], [1, 2, 3]];\n * const array2 = [[4, 5, 6], [7, 8, 9]];\n * const column = 2;\n * const res1 = UtlApp.insertColumns(array1, array2, column);\n * console.log(res1); // [[1,4,5,6,2,3],[1,7,8,9,2,3],[1,null,null,null,2,3]]\n *\n * const res2 = UtlApp.insertColumns(array1, array2, column, true);\n * console.log(res1); // [[1,4,5,6,2,3],[1,7,8,9,2,3],[1,null,null,null,2,3]]\n * ```\n *\n * @param {Array} array 2 dimensional array.\n * @param {Array} insertArray 2 dimensional array you want to insert.\n * @param {Number} column Column number you want to insert the array. The 1st number is 1. Default is 1.\n * @param {Boolean} rep When you want to replace the column, please use true. Default is false. In this case, the columns are inserted.\n * @param {Boolean} check Check whether the inputted array is 2 dimensional array. Default is true.\n * @return {Array} Array inserted the columns.\n */\nfunction insertColumns(\n  array,\n  insertArray,\n  column = 1,\n  rep = false,\n  check = true\n) {\n  if (check \u0026\u0026 !is2DimensionalArray(array)) {\n    throw new Error(\"Please use 2 dimensional array.\");\n  }\n  if (\n    !insertArray ||\n    !Array.isArray(insertArray) ||\n    insertArray.length == 0 ||\n    !is2DimensionalArray(insertArray)\n  ) {\n    throw new Error(\"Please set columns you want to insert.\");\n  }\n  const t = transpose(array, false);\n  t.splice(column - 1, rep ? 1 : 0, ...transpose(insertArray, false));\n  return transpose(t, false);\n}\n````\n\n\u003ca name=\"removeduplicatedvalues\"\u003e\u003c/a\u003e\n\n### removeDuplicatedValues\n\nRemove duplicated values from 1 dimensional array.\n\n````javascript\n/**\n * ### Description\n * Remove duplicated values from 1 dimensional array.\n *\n * ### Sample script\n * ```\n * const array1 = [\"a1\", \"b1\", \"c1\", \"b1\", \"c1\"];\n * const res = UtlApp.removeDuplicatedValues(array1);\n * console.log(res);\n * ```\n *\n * Result is as follows.\n *\n * ```\n * {\n * \"removeDuplicatedValues\":[\"a1\",\"b1\",\"c1\"],\n * \"duplicatedValues\":[\"b1\",\"c1\"],\n * \"numberOfDuplicate\":{\"a1\":1,\"b1\":2,\"c1\":2}\n * }\n * ```\n *\n * @param {Array} array 1 dimensional array.\n * @return {Object} Object including removeDuplicatedValues, duplicatedValues and numberOfDuplicate.\n */\nfunction removeDuplicatedValues(array) {\n  if (!Array.isArray(array)) {\n    throw new Error(\"Please use 1 dimensional array.\");\n  }\n  const obj = array.reduce(\n    (m, e) =\u003e m.set(e, m.has(e) ? m.get(e) + 1 : 1),\n    new Map()\n  );\n  const e = [...obj.entries()];\n  return {\n    removeDuplicatedValues: [...obj.keys()],\n    duplicatedValues: e.reduce((ar, [k, v]) =\u003e {\n      if (v != 1) ar.push(k);\n      return ar;\n    }, []),\n    numberOfDuplicate: Object.fromEntries(e),\n  };\n}\n````\n\n\u003ca name=\"get1stemptyrow\"\u003e\u003c/a\u003e\n\n### get1stEmptyRow\n\nRetrieve empty row index.\n\n````javascript\n/**\n * ### Description\n * Retrieve empty row index.\n *\n * ### Sample script\n * ```\n * const array1 = [[\"a1\"], [2], [3], [4], [], [5], [], [\"z\"], [\"\"], [7], [8]];\n * const res = UtlApp.get1stEmptyRow(array1);\n * console.log(res); // { topEmptyRow: 4, lastEmptyRow: 8 }\n * ```\n *\n * @param {Array} array 2 dimensional array.\n * @param {Boolean} check Check whether the inputted array is 2 dimensional array. Default is true.\n * @return {Object} Object including the top empty row index and the last empty row index.\n */\nfunction get1stEmptyRow(array, check = true) {\n  if (check \u0026\u0026 !is2DimensionalArray(array)) {\n    throw new Error(\"Please use 2 dimensional array.\");\n  }\n  return {\n    topEmptyRow: array.findIndex((r) =\u003e r.join(\"\") == \"\"),\n    lastEmptyRow:\n      array.length - 1 - array.reverse().findIndex((r) =\u003e r.join(\"\") == \"\"),\n  };\n}\n````\n\n\u003ca name=\"get1stemptycolumn\"\u003e\u003c/a\u003e\n\n### get1stEmptyColumn\n\nRetrieve empty column index.\n\n````javascript\n/**\n * ### Description\n * Retrieve empty column index.\n *\n * ### Sample script\n * ```\n * const array1 = [[\"a1\", \"b1\", \"f\", \"d1\", \"aa\", \"h\", \"aa\"], [2, 3, 4, 5], [3, 1, 1, , , 3], [4, 1, 1, 1], [1, 2, 3], [5, 1, 1], [1, 1, 1], [\"z\", 1, 1], [1, \"b\", 1, 1, \"\"], [7, 1, 1, 1], [8, 6, 5, 4, 3]];\n * const res = UtlApp.get1stEmptyColumn(array1);\n * console.log(res); // { topEmptyColumn: 3, lastEmptyColumn: 7 }\n * ```\n *\n * @param {Array} array 2 dimensional array.\n * @param {Boolean} check Check whether the inputted array is 2 dimensional array. Default is true.\n * @return {Object} Object including the top empty column index and the last empty column index.\n */\nfunction get1stEmptyColumn(array, check = true) {\n  if (check \u0026\u0026 !is2DimensionalArray(array)) {\n    throw new Error(\"Please use 2 dimensional array.\");\n  }\n  return array.reduce(\n    (o, r) =\u003e {\n      let top = r.findIndex(\n        (c) =\u003e typeof c === \"undefined\" || c.toString() == \"\"\n      );\n      let last =\n        r.length -\n        1 -\n        r\n          .reverse()\n          .findIndex((c) =\u003e typeof c === \"undefined\" || c.toString() == \"\");\n      top = top == -1 ? r.length : top;\n      last = last == -1 ? 0 : last;\n      o.topEmptyColumn = o.topEmptyColumn \u003c top ? o.topEmptyColumn : top;\n      o.lastEmptyColumn = o.lastEmptyColumn \u003e last ? o.lastEmptyColumn : last;\n      return o;\n    },\n    { topEmptyColumn: Infinity, lastEmptyColumn: 0 }\n  );\n}\n````\n\n\u003ca name=\"sum\"\u003e\u003c/a\u003e\n\n### sum\n\nSum numbers in an array.\n\n````javascript\n/**\n * ### Description\n * Sum numbers in an array.\n *\n * ### Sample script\n * ```\n * const array1 = [1, 2, 3, 4, 5];\n * const res = UtlApp.sum(array1);\n * console.log(res); // 15\n * ```\n *\n * @param {Array} array Input 1 dimensional array including number values.\n * @param {Boolean} check Check whether the values of the inputted array is number. Default is true.\n * @return {String} int8array.\n */\nfunction sum(array, check = true) {\n  if (check \u0026\u0026 !(Array.isArray(array) \u0026\u0026 array.every((e) =\u003e !isNaN(e)))) {\n    throw new Error(\"Please give an array including numbers.\");\n  }\n  return array.reduce((n, e) =\u003e (n += e), 0);\n}\n````\n\n\u003ca name=\"compilingnumbers\"\u003e\u003c/a\u003e\n\n### compilingNumbers\n\nCompiling Continuous Numbers using Google Apps Script. [Ref](https://tanaikech.github.io/2021/10/08/compiling-continuous-numbers-using-google-apps-script/)\n\n````javascript\n/**\n * ### Description\n * Compiling Continuous Numbers using Google Apps Script.\n * Ref: https://tanaikech.github.io/2021/10/08/compiling-continuous-numbers-using-google-apps-script/\n *\n * ### Sample script\n * ```\n * const ar = [4, 5, 9, 3, 10, 5, 11, 7, 7, 13, 1];\n * const res = UtlApp.compilingNumbers(ar);\n * console.log(res)\n * ```\n *\n * Result is as follows.\n *\n * ```\n * [\n *   { start: 1, end: 1 },\n *   { start: 3, end: 5 },\n *   { start: 7, end: 7 },\n *   { start: 9, end: 11 },\n *   { start: 13, end: 13 }\n * ]\n * ```\n *\n * @param {Array} array Input array.\n * @return {Array} Array including object like [{\"start\":1,\"end\":1},{\"start\":3,\"end\":5},{\"start\":7,\"end\":7},{\"start\":9,\"end\":11},{\"start\":13,\"end\":13}].\n */\nfunction compilingNumbers(array) {\n  if (!(Array.isArray(array) \u0026\u0026 array.every((e) =\u003e !isNaN(e)))) {\n    throw new Error(\"Please give an array including numbers.\");\n  }\n  const { values } = [...new Set(array.sort((a, b) =\u003e a - b))].reduce(\n    (o, e, i, a) =\u003e {\n      if (\n        o.temp.length == 0 ||\n        (o.temp.length \u003e 0 \u0026\u0026 e == o.temp[o.temp.length - 1] + 1)\n      ) {\n        o.temp.push(e);\n      } else {\n        if (o.temp.length \u003e 0) {\n          o.values.push({ start: o.temp[0], end: o.temp[o.temp.length - 1] });\n        }\n        o.temp = [e];\n      }\n      if (i == a.length - 1) {\n        o.values.push(\n          o.temp.length \u003e 1\n            ? { start: o.temp[0], end: o.temp[o.temp.length - 1] }\n            : { start: e, end: e }\n        );\n      }\n      return o;\n    },\n    { temp: [], values: [] }\n  );\n  return values;\n}\n````\n\n\u003ca name=\"convarraytoobject\"\u003e\u003c/a\u003e\n\n### convArrayToObject\n\nConverting 2 dimensional array to JSON object. [Ref](https://tanaikech.github.io/2021/10/24/converting-values-of-google-spreadsheet-to-object-using-google-apps-script/)\n\n````javascript\n/**\n * ### Description\n * Converting 2 dimensional array to JSON object.\n * Ref: https://tanaikech.github.io/2021/10/24/converting-values-of-google-spreadsheet-to-object-using-google-apps-script/\n *\n * ### Sample script\n * ```\n * const headers = [\"header1\", \"header2\", \"header3\"];\n * const rows = [[\"a2\", \"b2\", \"c2\"], [\"a3\", \"b3\", \"c3\"]];\n * const res = UtlApp.convArrayToObject(headers, rows);\n * console.log(res);\n * ```\n *\n * Result is as follows.\n *\n * ```\n * [\n *   { header1: 'a2', header2: 'b2', header3: 'c2' },\n *   { header1: 'a3', header2: 'b3', header3: 'c3' }\n * ]\n * ```\n *\n * @param {Array} headers Header array (1 dimensional array).\n * @param {Array} rows Row array (2 dimensional array).\n * @return {Object} JSON object.\n */\nfunction convArrayToObject(headers, rows) {\n  if (\n    !Array.isArray(headers) ||\n    !Array.isArray(rows) ||\n    !is2DimensionalArray(rows)\n  ) {\n    throw new Error(\"Please give an array of header and values.\");\n  }\n  return rows.map((r) =\u003e headers.reduce((o, h, j) =\u003e ((o[h] = r[j]), o), {}));\n}\n````\n\n\u003ca name=\"unpivot\"\u003e\u003c/a\u003e\n\n### unpivot\n\nConverting 2-dimensional array as unpivot (reverse pivot). [Ref](https://tanaikech.github.io/2023/05/11/unpivot-on-google-spreadsheet-using-google-apps-script/)\n\n````javascript\n/**\n * ### Description\n * Converting 2-dimensional array as unpivot (reverse pivot).\n *\n * ### Sample script\n * ```\n * const values = [\n *   [\"\", \"b1\", \"c1\", \"d1\"],\n *   [\"a2\", 1, 2, 3],\n *   [\"a3\", 4, 5, 6],\n *   [\"a4\", 7, 8, 9],\n *   [\"a5\", 10, 11, 12]\n * ];\n * const res = UtlApp.unpivot(values);\n * console.log(res);\n * ```\n *\n * Result is as follows.\n *\n * ```\n * [\n *   ['b1', 'a2', 1],\n *   ['b1', 'a3', 4],\n *   ['b1', 'a4', 7],\n *   ['b1', 'a5', 10],\n *   ['c1', 'a2', 2],\n *   ['c1', 'a3', 5],\n *   ['c1', 'a4', 8],\n *   ['c1', 'a5', 11],\n *   ['d1', 'a2', 3],\n *   ['d1', 'a3', 6],\n *   ['d1', 'a4', 9],\n *   ['d1', 'a5', 12]\n * ]\n * ```\n *\n * @param {Array} values 2 dimensional array.\n * @return {Array} 2 dimensional array converted as unpivot (reverse pivot).\n */\nfunction unpivot(values) {\n  if (!Array.isArray(values) || !is2DimensionalArray(values)) {\n    throw new Error(\"Please give an array of values.\");\n  }\n  const [[, ...h], ...v] = values;\n  return h.flatMap((hh, i) =\u003e v.map((t) =\u003e [hh, t[0], t[i + 1]]));\n}\n````\n\n\u003ca name=\"reverseunpivot\"\u003e\u003c/a\u003e\n\n### reverseUnpivot\n\nReversing 2-dimensional array with unpivot. [Ref](https://tanaikech.github.io/2023/05/11/unpivot-on-google-spreadsheet-using-google-apps-script/)\n\n````javascript\n/**\n * ### Description\n * Reversing 2-dimensional array with unpivot.\n * Ref: https://tanaikech.github.io/2023/05/11/unpivot-on-google-spreadsheet-using-google-apps-script/\n *\n * ### Sample script\n * ```\n * const values = [\n *   [\"b1\", \"a2\", 1],\n *   [\"b1\", \"a3\", 4],\n *   [\"b1\", \"a4\", 7],\n *   [\"b1\", \"a5\", 10],\n *   [\"c1\", \"a2\", 2],\n *   [\"c1\", \"a3\", 5],\n *   [\"c1\", \"a4\", 8],\n *   [\"c1\", \"a5\", 11],\n *   [\"d1\", \"a2\", 3],\n *   [\"d1\", \"a3\", 6],\n *   [\"d1\", \"a4\", 9],\n *   [\"d1\", \"a5\", 12]\n * ];\n * const res = UtlApp.reverseUnpivot(values);\n * console.log(res);\n * ```\n *\n * Result is as follows.\n *\n * ```\n * [\n *   [null, 'b1', 'c1', 'd1'],\n *   ['a2', 1, 2, 3],\n *   ['a3', 4, 5, 6],\n *   ['a4', 7, 8, 9],\n *   ['a5', 10, 11, 12]\n * ]\n * ```\n *\n * @param {Array} values 2 dimensional array.\n * @return {Array} Reversed 2 dimensional array with unpivot.\n */\nfunction reverseUnpivot(values) {\n  if (!Array.isArray(values) || !is2DimensionalArray(values)) {\n    throw new Error(\"Please give an array of values.\");\n  }\n  const [a, b, c] = values[0].map((_, c) =\u003e values.map((r) =\u003e r[c]));\n  const ch = [...new Set(a)];\n  const rh = [...new Set(b)];\n  const size = rh.length;\n  const temp = [\n    [null, ...rh],\n    ...[...Array(Math.ceil(c.length / size))]\n      .map((_) =\u003e c.splice(0, size))\n      .map((vv, i) =\u003e [ch[i], ...vv]),\n  ];\n  return temp[0].map((_, c) =\u003e temp.map((r) =\u003e r[c]));\n}\n````\n\n\u003ca name=\"dotproduct\"\u003e\u003c/a\u003e\n\n### dotProduct\n\nCalculate dot product from 2 arrays.\n\n````javascript\n/**\n * ### Description\n * Calculate dot product from 2 arrays.\n *\n * ### Sample script\n * ```\n * const array1 = [1, 2, 3, 4, 5];\n * const array2 = [3, 4, 5, 6, 7];\n * const res = UtlApp.dotProduct(array1, array2);\n * ```\n *\n * @param {Array} array1 1-dimensional array including numbers.\n * @param {Array} array2 1-dimensional array including numbers.\n * @return {Number} Calculated result of dot product.\n */\nfunction dotProduct(array1, array2) {\n  if (!Array.isArray(array1) || !Array.isArray(array2)) {\n    throw new Error(\"Please give 2 arrays.\");\n  }\n  return array1.reduce((t, e, i) =\u003e (t += e * array2[i]), 0);\n}\n````\n\n\u003ca name=\"cosinesimilarity\"\u003e\u003c/a\u003e\n\n### cosineSimilarity\n\nCalculate cosine similarity from 2 arrays. This can be used with \"[Semantic Search using Gemini Pro API with Google Apps Script](https://medium.com/google-cloud/semantic-search-using-gemini-pro-api-with-google-apps-script-5e511e26eaf0)\" of my report.\n\n````javascript\n/**\n * ### Description\n * Calculate cosine similarity from 2 arrays.\n *\n * ### Sample script\n * ```\n * const array1 = [1, 2, 3, 4, 5];\n * const array2 = [3, 4, 5, 6, 7];\n * const res = UtlApp.cosineSimilarity(array1, array2);\n * ```\n *\n * @param {Array} array1 1-dimensional array including numbers.\n * @param {Array} array2 1-dimensional array including numbers.\n * @return {Number} Calculated result of cosine similarity.\n */\nfunction cosineSimilarity(array1, array2) {\n  if (!Array.isArray(array1) || !Array.isArray(array2)) {\n    throw new Error(\"Please give 2 arrays.\");\n  }\n  const dotProduct = array1.reduce((t, e, i) =\u003e (t += e * array2[i]), 0);\n  const magnitudes = [array1, array2]\n    .map((e) =\u003e Math.sqrt(e.reduce((t, f) =\u003e (t += f * f), 0)))\n    .reduce((t, f) =\u003e (t *= f), 1);\n  return dotProduct / magnitudes;\n}\n````\n\n---\n\n## For binary processing\n\n\u003ca name=\"convInt8arraytohexar\"\u003e\u003c/a\u003e\n\n### convInt8ArrayToHexAr\n\nConvert Int8Array to hex string array. [Ref](https://tanaikech.github.io/2021/12/18/converting-from-string-to-hex-from-hex-to-bytes-from-bytes-to-string-using-google-apps-script/)\n\n````javascript\n/**\n * ### Description\n * Convert Int8Array to hex string array.\n * Ref: https://tanaikech.github.io/2021/12/18/converting-from-string-to-hex-from-hex-to-bytes-from-bytes-to-string-using-google-apps-script/\n *\n * ### Sample script\n * ```\n * const ar = Utilities.newBlob(\"さんぷる\").getBytes();\n * const res = UtlApp.convInt8ArrayToHexAr(ar);\n * console.log(res); // [ 'e3', '81', '95', 'e3', '82', '93', 'e3', '81', 'b7', 'e3', '82', '8b' ]\n * ```\n *\n * @param {Array} array int8 array.\n * @return {Array} Hex string array.\n */\nfunction convInt8ArrayToHexAr(array) {\n  if (!Array.isArray(array)) {\n    throw new Error(\"Please give Int8Array.\");\n  }\n  return array.map((byte) =\u003e (\"0\" + (byte \u0026 0xff).toString(16)).slice(-2));\n}\n````\n\n\u003ca name=\"convstrtohex\"\u003e\u003c/a\u003e\n\n### convStrToHex\n\nConvert string to hex. [Ref](https://tanaikech.github.io/2021/12/18/converting-from-string-to-hex-from-hex-to-bytes-from-bytes-to-string-using-google-apps-script/)\n\n````javascript\n/**\n * ### Description\n * Convert string to hex.\n * Ref: https://tanaikech.github.io/2021/12/18/converting-from-string-to-hex-from-hex-to-bytes-from-bytes-to-string-using-google-apps-script/\n *\n * ### Sample script\n * ```\n * const str = \"sample string\";\n * const res = UtlApp.convStrToHex(str);\n * console.log(res); // 73616d706c6520737472696e67\n * ```\n *\n * @param {String} str Input string value.\n * @return {String} Hex string value.\n */\nfunction convStrToHex(str) {\n  if (!str) {\n    throw new Error(\"Please give a string value.\");\n  }\n  return convInt8ArrayToHexAr(Utilities.newBlob(str).getBytes()).join(\"\");\n}\n````\n\n\u003ca name=\"convhextoInt8ar\"\u003e\u003c/a\u003e\n\n### convHexToInt8Ar\n\nConvert string to hex. [Ref](https://tanaikech.github.io/2021/12/18/converting-from-string-to-hex-from-hex-to-bytes-from-bytes-to-string-using-google-apps-script/)\n\n````javascript\n/**\n * ### Description\n * Convert string to hex.\n * Ref: https://tanaikech.github.io/2021/12/18/converting-from-string-to-hex-from-hex-to-bytes-from-bytes-to-string-using-google-apps-script/\n *\n * ### Sample script\n * ```\n * const hex = \"73616d706c6520737472696e67\";\n * const res = UtlApp.convHexToInt8Ar(hex);\n * console.log(res); // [ 115, 97, 109, 112, 108, 101, 32, 115, 116, 114, 105, 110, 103 ]\n *\n * console.log(UtlApp.convInt8ArToStr(res)); // sample string\n * ```\n *\n * @param {String} hex Input hex string value.\n * @return {Array} Converted int8array.\n */\nfunction convHexToInt8Ar(hex) {\n  if (!hex) {\n    throw new Error(\"Please give a hex string value.\");\n  }\n  return hex\n    .match(/.{2}/g)\n    .map((e) =\u003e\n      parseInt(e[0], 16).toString(2).length == 4\n        ? parseInt(e, 16) - 256\n        : parseInt(e, 16)\n    );\n}\n````\n\n\u003ca name=\"convint8artostr\"\u003e\u003c/a\u003e\n\n### convInt8ArToStr\n\nConvert Int8Array to string value.\n\n````javascript\n/**\n * ### Description\n * Convert Int8Array to string value.\n *\n * ### Sample script\n * ```\n * const hex = \"73616d706c6520737472696e67\";\n * const res = UtlApp.convHexToInt8Ar(hex);\n * console.log(res); // [ 115, 97, 109, 112, 108, 101, 32, 115, 116, 114, 105, 110, 103 ]\n *\n * console.log(UtlApp.convInt8ArToStr(res)); // sample string\n * ```\n *\n * @param {Array} array Input int8array.\n * @return {String} Converted string value.\n */\nfunction convInt8ArToStr(array) {\n  if (!Array.isArray(array)) {\n    throw new Error(\"Please give Int8Array.\");\n  }\n  return Utilities.newBlob(array).getDataAsString();\n}\n````\n\n\u003ca name=\"convint8artouint8ar\"\u003e\u003c/a\u003e\n\n### convInt8ArToUint8Ar\n\nConvert Int8Array to Uint8Array.\n\n````javascript\n/**\n * ### Description\n * Convert Int8Array to Uint8Array.\n *\n * ### Sample script\n * ```\n * const str = \"さんぷる\";\n * const int8Ar = Utilities.newBlob(str).getBytes();\n * console.log(int8Ar); // [ -29, -127, -107, -29, -126, -109, -29, -127, -73, -29, -126, -117 ]\n *\n * const res1 = UtlApp.convInt8ArToUint8Ar(int8Ar);\n * console.log(res1); // [ 227, 129, 149, 227, 130, 147, 227, 129, 183, 227, 130, 139 ]\n *\n * const res2 = UtlApp.convUint8ArToInt8Ar(res1);\n * console.log(res2); // [ -29, -127, -107, -29, -126, -109, -29, -127, -73, -29, -126, -117 ]\n * ```\n *\n * @param {Array} array Input int8array.\n * @return {Array} unit8array.\n */\nfunction convInt8ArToUint8Ar(array) {\n  if (!Array.isArray(array)) {\n    throw new Error(\"Please give Int8Array.\");\n  }\n  return [...Uint8Array.from(array)];\n}\n````\n\n\u003ca name=\"convuint8artoInt8ar\"\u003e\u003c/a\u003e\n\n### convUint8ArToInt8Ar\n\nConvert Uint8Array to Int8Array.\n\n````javascript\n/**\n * ### Description\n * Convert Uint8Array to Int8Array.\n *\n * ### Sample script\n * ```\n * const str = \"さんぷる\";\n * const int8Ar = Utilities.newBlob(str).getBytes();\n * console.log(int8Ar); // [ -29, -127, -107, -29, -126, -109, -29, -127, -73, -29, -126, -117 ]\n *\n * const res1 = UtlApp.convInt8ArToUint8Ar(int8Ar);\n * console.log(res1); // [ 227, 129, 149, 227, 130, 147, 227, 129, 183, 227, 130, 139 ]\n *\n * const res2 = UtlApp.convUint8ArToInt8Ar(res1);\n * console.log(res2); // [ -29, -127, -107, -29, -126, -109, -29, -127, -73, -29, -126, -117 ]\n * ```\n *\n * @param {Array} array Input unit8array.\n * @return {Array} int8array.\n */\nfunction convUint8ArToInt8Ar(array) {\n  if (!Array.isArray(array)) {\n    throw new Error(\"Please give Uint8Array.\");\n  }\n  return [...Int8Array.from(array)];\n}\n````\n\n\u003ca name=\"searchIndexfromdatabydata\"\u003e\u003c/a\u003e\n\n### searchIndexFromDataByData\n\nSearch index from base data using a search data.\n\n````javascript\n/**\n * ### Description\n * Search index from base data using a search data.\n *\n * ### Sample script\n * ```\n * const sampleString = \"abc123def123ghi123jkl\";\n * const splitValue = \"123\";\n * const baseData = Utilities.newBlob(sampleString).getBytes();\n * const searchData = Utilities.newBlob(splitValue).getBytes();\n * const res = UtlApp.searchIndexFromDataByData(baseData, searchData);\n * console.log(res); // 3\n * ```\n *\n * @param {Array} baseData Input byteArray of base data.\n * @param {Array} searchData Input byteArray of search data using split.\n * @return {Number} Index of search data. If the search data is not found, -1 is returned.\n */\nfunction searchIndexFromDataByData(baseData, searchData) {\n  if (!Array.isArray(baseData) || !Array.isArray(searchData)) {\n    throw new Error(\"Please give Int8Array.\");\n  }\n  const search = searchData.join(\"\");\n  const bLen = searchData.length;\n  return baseData.findIndex(\n    (_, i, a) =\u003e [...Array(bLen)].map((_, j) =\u003e a[j + i]).join(\"\") == search\n  );\n}\n````\n\n\u003ca name=\"splitbytearraybysearchdata\"\u003e\u003c/a\u003e\n\n### splitByteArrayBySearchData\n\nSplit byteArray by a search data. [Ref](https://tanaikech.github.io/2023/03/08/split-binary-data-with-search-data-using-google-apps-script/)\n\n````javascript\n/**\n * ### Description\n * Split byteArray by a search data.\n *\n * ### Sample script\n * ```\n * const sampleString = \"abc123def123ghi123jkl\";\n * const splitValue = \"123\";\n * const baseData = Utilities.newBlob(sampleString).getBytes();\n * const searchData = Utilities.newBlob(splitValue).getBytes();\n * const res = UtlApp.splitByteArrayBySearchData(baseData, searchData);\n * console.log(res);\n * ```\n *\n * Result is as follows.\n *\n * ```\n * [\n *   [ 97, 98, 99 ],\n *   [ 100, 101, 102 ],\n *   [ 103, 104, 105 ],\n *   [ 106, 107, 108 ]\n * ]\n * ```\n *\n * @param {Array} baseData Input byteArray of base data.\n * @param {Array} searchData Input byteArray of search data using split.\n * @return {Array} An array including byteArray.\n */\nfunction splitByteArrayBySearchData(baseData, searchData) {\n  if (!Array.isArray(baseData) || !Array.isArray(searchData)) {\n    throw new Error(\"Please give Int8Array.\");\n  }\n  const search = searchData.join(\"\");\n  const bLen = searchData.length;\n  const res = [];\n  let idx = 0;\n  do {\n    idx = baseData.findIndex(\n      (_, i, a) =\u003e [...Array(bLen)].map((_, j) =\u003e a[j + i]).join(\"\") == search\n    );\n    if (idx != -1) {\n      res.push(baseData.splice(0, idx));\n      baseData.splice(0, bLen);\n    } else {\n      res.push(baseData.splice(0));\n    }\n  } while (idx != -1);\n  return res;\n}\n````\n\n---\n\n## For string processing\n\n\u003ca name=\"convtext\"\u003e\u003c/a\u003e\n\n### ConvText\n\nConverting text as unicode. [Ref](https://tanaikech.github.io/2020/11/13/converting-texts-to-bold-italic-and-bold-italic-types-of-unicode-using-google-apps-script/)\n\n````javascript\n/**\n * @namespace ConvText\n *\n * ### Description\n * Converting text as unicode.\n * Ref: https://tanaikech.github.io/2020/11/13/converting-texts-to-bold-italic-and-bold-italic-types-of-unicode-using-google-apps-script/\n *\n * ### Sample script\n * ```\n * const text = \"Sample value 123\\nSample value 456\\nSample value 789\";\n * console.log(text); // Original text\n * console.log(UtlApp.ConvText.bold(text)); // Bold type\n * console.log(UtlApp.ConvText.italic(text)); // Italic type\n * console.log(UtlApp.ConvText.boldItalic(text)); // Bold-italic type\n * console.log(UtlApp.ConvText.underLine(text)); // Underline\n * console.log(UtlApp.ConvText.strikethrough(text)); // Strikethrough\n * ```\n *\n */\nvar ConvText = {\n  /**\n   * Method for processing\n   * @param {String} text\n   * @param {Object} obj\n   * @return {String} Converted text.\n   */\n  c_: function (text, obj) {\n    return text.replace(\n      new RegExp(`[${obj.reduce((s, { r }) =\u003e (s += r), \"\")}]`, \"g\"),\n      (e) =\u003e {\n        const t = e.codePointAt(0);\n        if (\n          (t \u003e= 48 \u0026\u0026 t \u003c= 57) ||\n          (t \u003e= 65 \u0026\u0026 t \u003c= 90) ||\n          (t \u003e= 97 \u0026\u0026 t \u003c= 122)\n        ) {\n          return obj.reduce((s, { r, d }) =\u003e {\n            if (new RegExp(`[${r}]`).test(e))\n              s = String.fromCodePoint(e.codePointAt(0) + d);\n            return s;\n          }, \"\");\n        }\n        return e;\n      }\n    );\n  },\n\n  /**\n   * Convert text to bold type.\n   * @param {String} text\n   * @return {String} Converted text.\n   */\n  bold: function (text) {\n    return this.c_(text, [\n      { r: \"0-9\", d: 120734 },\n      { r: \"A-Z\", d: 120211 },\n      { r: \"a-z\", d: 120205 },\n    ]);\n  },\n\n  /**\n   * Convert text to italic type.\n   * Ref: https://tanaikech.github.io/2020/11/13/converting-texts-to-bold-italic-and-bold-italic-types-of-unicode-using-google-apps-script/\n   * @param {String} text\n   * @return {String} Converted text.\n   */\n  italic: function (text) {\n    return this.c_(text, [\n      { r: \"A-Z\", d: 120263 },\n      { r: \"a-z\", d: 120257 },\n    ]);\n  },\n\n  /**\n   * Convert text to bold and italic type.\n   * @param {String} text\n   * @return {String} Converted text.\n   */\n  boldItalic: function (text) {\n    return this.c_(text, [\n      { r: \"A-Z\", d: 120315 },\n      { r: \"a-z\", d: 120309 },\n    ]);\n  },\n\n  /**\n   * Convert text to underline type.\n   * @param {String} text\n   * @return {String} Converted text.\n   */\n  underLine: function (text) {\n    return text.length \u003e 0 ? [...text].join(\"\\u0332\") + \"\\u0332\" : \"\";\n  },\n\n  /**\n   * Convert text to strikethrough type.\n   * @param {String} text\n   * @return {String} Converted text.\n   */\n  strikethrough: function (text) {\n    return text.length \u003e 0 ? [...text].join(\"\\u0336\") + \"\\u0336\" : \"\";\n  },\n};\n````\n\n#### Additional information\n\nAs additional information, when you want to convert the above exported text to the normal text, you can use [normalize()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/normalize). The sample script can be seen at [my answer on Stackoverflow](https://stackoverflow.com/a/79108290).\n\n\u003ca name=\"columnlettertoIndex\"\u003e\u003c/a\u003e\n\n### columnLetterToIndex\n\nConverting colum letter to column index. Start of column index is 0. [Ref](https://tanaikech.github.io/2022/05/01/increasing-column-letter-by-one-using-google-apps-script/)\n\n````javascript\n/**\n * ### Description\n * Converting colum letter to column index. Start of column index is 0.\n * Ref: https://tanaikech.github.io/2022/05/01/increasing-column-letter-by-one-using-google-apps-script/\n *\n * ### Sample script\n * ```\n * const res1 = UtlApp.columnLetterToIndex(\"Z\");\n * console.log(res1); // 25\n * const res2 = UtlApp.columnLetterToIndex(\"AA\");\n * console.log(res2); // 26\n * ```\n *\n * @param {String} letter Column letter.\n * @return {Number} Column index.\n */\nfunction columnLetterToIndex(letter = null) {\n  if (letter === null || typeof letter != \"string\") {\n    throw new Error(\"Please give the column letter as a string.\");\n  }\n  letter = letter.toUpperCase();\n  return [...letter].reduce(\n    (c, e, i, a) =\u003e\n      (c += (e.charCodeAt(0) - 64) * Math.pow(26, a.length - i - 1)),\n    -1\n  );\n}\n````\n\n\u003ca name=\"columnIndextoletter\"\u003e\u003c/a\u003e\n\n### columnIndexToLetter\n\nConverting colum index to column letter. Start of column index is 0. [Ref](https://stackoverflow.com/a/53678158/7108653)\n\n````javascript\n/**\n * ### Description\n * Converting colum index to column letter. Start of column index is 0.\n * Ref: https://stackoverflow.com/a/53678158/7108653\n *\n * ### Sample script\n * ```\n * const res1 = UtlApp.columnIndexToLetter(res1);\n * console.log(res1); // Z\n * const res2 = UtlApp.columnIndexToLetter(res2);\n * console.log(res2); // AA\n * ```\n *\n * @param {Number} index Column index.\n * @return {String} Column letter.\n */\nfunction columnIndexToLetter(index = null) {\n  if (index === null || isNaN(index)) {\n    throw new Error(\n      \"Please give the column indexr as a number. In this case, 1st number is 0.\"\n    );\n  }\n  return (a = Math.floor(index / 26)) \u003e= 0\n    ? columnIndexToLetter(a - 1) + String.fromCharCode(65 + (index % 26))\n    : \"\";\n}\n````\n\n\u003ca name=\"conva1notationtogridrange\"\u003e\u003c/a\u003e\n\n### convA1NotationToGridRange\n\nConverting a1Notation to gridrange. This will be useful for using Sheets API. [Ref](https://tanaikech.github.io/2017/07/31/converting-a1notation-to-gridrange-for-google-sheets-api/)\n\n````javascript\n/**\n * ### Description\n * Converting a1Notation to gridrange. This will be useful for using Sheets API.\n * Ref: https://tanaikech.github.io/2017/07/31/converting-a1notation-to-gridrange-for-google-sheets-api/\n *\n * ### Sample script\n * ```\n * const a1Notation = \"AB25:AD51\";\n * const sheetId = 0;\n * const res = UtlApp.convA1NotationToGridRange(a1Notation, sheetId);\n * console.log(res);\n * ```\n *\n * Result is as follows.\n *\n * ```\n * {\n *   sheetId: 0,\n *   startRowIndex: 24,\n *   endRowIndex: 51,\n *   startColumnIndex: 27,\n *   endColumnIndex: 30\n * }\n * ```\n *\n * @param {String} a1Notation A1Notation of range.\n * @param {Number} sheetId Sheet ID of the range.\n * @return {Object} Gridrange.\n */\nfunction convA1NotationToGridRange(a1Notation = null, sheetId = null) {\n  if (\n    a1Notation === null ||\n    sheetId === null ||\n    typeof a1Notation != \"string\" ||\n    isNaN(sheetId)\n  ) {\n    throw new Error(\"Please give a1Notation (string) and sheet ID (integer).\");\n  }\n  const { col, row } = a1Notation\n    .toUpperCase()\n    .split(\"!\")\n    .map((f) =\u003e f.split(\":\"))\n    .pop()\n    .reduce(\n      (o, g) =\u003e {\n        var [r1, r2] = [\"[A-Z]+\", \"[0-9]+\"].map((h) =\u003e g.match(new RegExp(h)));\n        o.col.push(r1 \u0026\u0026 columnLetterToIndex(r1[0]));\n        o.row.push(r2 \u0026\u0026 Number(r2[0]));\n        return o;\n      },\n      { col: [], row: [] }\n    );\n  col.sort((a, b) =\u003e (a \u003e b ? 1 : -1));\n  row.sort((a, b) =\u003e (a \u003e b ? 1 : -1));\n  const [start, end] = col.map((e, i) =\u003e ({ col: e, row: row[i] }));\n  const obj = {\n    sheetId,\n    startRowIndex: start?.row \u0026\u0026 start.row - 1,\n    endRowIndex: end?.row ? end.row : start.row,\n    startColumnIndex: start \u0026\u0026 start.col,\n    endColumnIndex: end ? end.col + 1 : start.col + 1,\n  };\n  if (obj.startRowIndex === null) {\n    obj.startRowIndex = 0;\n    delete obj.endRowIndex;\n  }\n  if (obj.startColumnIndex === null) {\n    obj.startColumnIndex = 0;\n    delete obj.endColumnIndex;\n  }\n  return obj;\n}\n````\n\n\u003ca name=\"convgridrangetoa1Notation\"\u003e\u003c/a\u003e\n\n### convGridRangeToA1Notation\n\nConverting gridrange to a1Notation. This will be useful for using Sheets API. [Ref](https://tanaikech.github.io/2017/07/31/converting-a1notation-to-gridrange-for-google-sheets-api/)\n\n````javascript\n/**\n * ### Description\n * Converting gridrange to a1Notation. This will be useful for using Sheets API.\n * Ref: https://tanaikech.github.io/2017/07/31/converting-a1notation-to-gridrange-for-google-sheets-api/\n *\n * ### Sample script 1\n * ```\n * const gridRange = {\n *   sheetId: 0,\n *   startRowIndex: 24,\n *   endRowIndex: 51,\n *   startColumnIndex: 27,\n *   endColumnIndex: 30\n * };\n * const sheetName = \"Sheet1\";\n * const res = UtlApp.convGridRangeToA1Notation(gridRange, sheetName);\n * console.log(res); // 'Sheet1'!AB25:AD51\n * ```\n *\n * ### Sample script 2\n * ```\n * const gridRange = {\n *   sheetId: 0,\n *   startRowIndex: 24,\n *   endRowIndex: 51,\n *   startColumnIndex: 27,\n *   endColumnIndex: 30\n * };\n * const res = UtlApp.convGridRangeToA1Notation(gridRange);\n * console.log(res); // AB25:AD51\n * ```\n *\n * @param {Object} gridrange Gridrange of range.\n * @param {String} sheetName Sheet name of the range.\n * @return {String} A1Notation.\n */\nfunction convGridRangeToA1Notation(gridrange, sheetName = \"\") {\n  if (gridrange === null) {\n    throw new Error(\"Please give gridRange (JSON object).\");\n  }\n  const start = {};\n  const end = {};\n  if (gridrange.hasOwnProperty(\"startColumnIndex\")) {\n    start.col = columnIndexToLetter(gridrange.startColumnIndex);\n  } else if (\n    !gridrange.hasOwnProperty(\"startColumnIndex\") \u0026\u0026\n    gridrange.hasOwnProperty(\"endColumnIndex\")\n  ) {\n    start.col = \"A\";\n  }\n  if (gridrange.hasOwnProperty(\"startRowIndex\")) {\n    start.row = gridrange.startRowIndex + 1;\n  } else if (\n    !gridrange.hasOwnProperty(\"startRowIndex\") \u0026\u0026\n    gridrange.hasOwnProperty(\"endRowIndex\")\n  ) {\n    start.row = gridrange.endRowIndex;\n  }\n  if (gridrange.hasOwnProperty(\"endColumnIndex\")) {\n    end.col = columnIndexToLetter(gridrange.endColumnIndex - 1);\n  } else if (!gridrange.hasOwnProperty(\"endColumnIndex\")) {\n    end.col = \"{Here, please set the max column letter.}\";\n  }\n  if (gridrange.hasOwnProperty(\"endRowIndex\")) {\n    end.row = gridrange.endRowIndex;\n  }\n  const k = [\"col\", \"row\"];\n  const st = k.map((e) =\u003e start[e]).join(\"\");\n  const en = k.map((e) =\u003e end[e]).join(\"\");\n  if (sheetName) {\n    return st == en ? `'${sheetName}'!${st}` : `'${sheetName}'!${st}:${en}`;\n  }\n  return st == en ? `${st}` : `${st}:${en}`;\n}\n````\n\n\u003ca name=\"addqueryparameters\"\u003e\u003c/a\u003e\n\n### addQueryParameters\n\nThis method is used for adding the query parameters to the URL. [Ref](https://tanaikech.github.io/2018/07/12/adding-query-parameters-to-url-using-google-apps-script/)\n\n````javascript\n/**\n * ### Description\n * This method is used for adding the query parameters to the URL.\n * Ref: https://tanaikech.github.io/2018/07/12/adding-query-parameters-to-url-using-google-apps-script/\n *\n * ### Sample script 1\n * ```\n * const url = \"https://sampleUrl\";\n * const query = {\n *   query1: [\"value1A\", \"value1B\", \"value1C\"],\n *   query2: \"value2A, value2B\",\n *   query3: \"value3A/value3B\",\n * };\n * const endpoint = UtlApp.addQueryParameters(url, query);\n * console.log(endpoint); // https://sampleUrl?query1=value1A\u0026query1=value1B\u0026query1=value1C\u0026query2=value2A%2C%20value2B\u0026query3=value3A%2Fvalue3B\n * ```\n *\n * ### Sample script 2\n * ```\n * const url = \"\";\n * const query = {\n *   query1: [\"value1A\", \"value1B\", \"value1C\"],\n *   query2: \"value2A, value2B\",\n *   query3: \"value3A/value3B\",\n * };\n * const endpoint = UtlApp.addQueryParameters(url, query);\n * console.log(endpoint); // query1=value1A\u0026query1=value1B\u0026query1=value1C\u0026query2=value2A%2C%20value2B\u0026query3=value3A%2Fvalue3B\n * ```\n *\n * @param {String} url The base URL for adding the query parameters.\n * @param {Object} obj JSON object including query parameters.\n * @return {String} URL including the query parameters.\n */\nfunction addQueryParameters(url, obj) {\n  if (url === null || obj === null || typeof url != \"string\") {\n    throw new Error(\n      \"Please give URL (String) and query parameter (JSON object).\"\n    );\n  }\n  return (\n    (url == \"\" ? \"\" : `${url}?`) +\n    Object.entries(obj)\n      .flatMap(([k, v]) =\u003e\n        Array.isArray(v)\n          ? v.map((e) =\u003e `${k}=${encodeURIComponent(e)}`)\n          : `${k}=${encodeURIComponent(v)}`\n      )\n      .join(\"\u0026\")\n  );\n}\n````\n\n- In this case, when you use `\"\"` as `url`, only the query parameters are returned.\n\n\u003ca name=\"parsequeryparameters\"\u003e\u003c/a\u003e\n\n### parseQueryParameters\n\nThis method is used for parsing the URL including the query parameters. [Ref](https://tanaikech.github.io/2018/07/12/adding-query-parameters-to-url-using-google-apps-script/)\n\n````javascript\n/**\n * ### Description\n * This method is used for parsing the URL including the query parameters.\n * Ref: https://tanaikech.github.io/2018/07/12/adding-query-parameters-to-url-using-google-apps-script/\n *\n * ### Sample script\n * ```\n * const url = \"https://sampleUrl.com/sample?key1=value1\u0026key2=value2\u0026key1=value3\u0026key3=value4\u0026key2=value5\";\n * const res = UtlApp.parseQueryParameters(url);\n * console.log(res);\n * ```\n *\n * Result is as follows.\n *\n * ```\n * {\n *   url: 'https://sampleUrl.com/sample',\n *   queryParameters:\n *   {\n *     key1: ['value1', 'value3'],\n *     key2: ['value2', 'value5'],\n *     key3: ['value4']\n *   }\n * }\n * ```\n *\n * @param {String} url The URL including the query parameters.\n * @return {Object} JSON object including the base url and the query parameters.\n */\nfunction parseQueryParameters(url) {\n  if (url === null || typeof url != \"string\") {\n    throw new Error(\"Please give URL (String) including the query parameters.\");\n  }\n  const s = url.split(\"?\");\n  if (s.length == 1) {\n    return { url: s[0], queryParameters: null };\n  }\n  const [baseUrl, query] = s;\n  if (query) {\n    const queryParameters = query.split(\"\u0026\").reduce(function (o, e) {\n      const temp = e.split(\"=\");\n      const key = temp[0].trim();\n      let value = temp[1].trim();\n      value = isNaN(value) ? value : Number(value);\n      if (o[key]) {\n        o[key].push(value);\n      } else {\n        o[key] = [value];\n      }\n      return o;\n    }, {});\n    return { url: baseUrl, queryParameters };\n  }\n  return null;\n}\n````\n\n\u003ca name=\"expanda1notations\"\u003e\u003c/a\u003e\n\n### expandA1Notations\n\nThis method is used for expanding A1Notations. [Ref](https://tanaikech.github.io/2020/04/04/updated-expanding-a1notations-using-google-apps-script/)\n\n````javascript\n/**\n * ### Description\n * This method is used for expanding A1Notations.\n * Ref: https://tanaikech.github.io/2020/04/04/updated-expanding-a1notations-using-google-apps-script/\n *\n * ### Sample script\n * ```\n * const a1Notations = [\"A1:E3\", \"B10:W13\", \"EZ5:FA8\", \"AAA1:AAB3\"];\n * const res = UtlApp.expandA1Notations(a1Notations);\n * console.log(JSON.stringify(res));\n * ```\n *\n * Result is as follows.\n *\n * ```\n * [\n *   [\"A1\",\"B1\",\"C1\",\"D1\",\"E1\",\"A2\",\"B2\",\"C2\",\"D2\",\"E2\",\"A3\",\"B3\",\"C3\",\"D3\",\"E3\"],\n *   [\"B10\",\"C10\",\"D10\",\"E10\",\"F10\",\"G10\",\"H10\",\"I10\",\"J10\",\"K10\",\"L10\",\"M10\",\"N10\",\"O10\",\"P10\",\"Q10\",\"R10\",\"S10\",\"T10\",\"U10\",\"V10\",\"W10\",\"B11\",\"C11\",\"D11\",\"E11\",\"F11\",\"G11\",\"H11\",\"I11\",\"J11\",\"K11\",\"L11\",\"M11\",\"N11\",\"O11\",\"P11\",\"Q11\",\"R11\",\"S11\",\"T11\",\"U11\",\"V11\",\"W11\",\"B12\",\"C12\",\"D12\",\"E12\",\"F12\",\"G12\",\"H12\",\"I12\",\"J12\",\"K12\",\"L12\",\"M12\",\"N12\",\"O12\",\"P12\",\"Q12\",\"R12\",\"S12\",\"T12\",\"U12\",\"V12\",\"W12\",\"B13\",\"C13\",\"D13\",\"E13\",\"F13\",\"G13\",\"H13\",\"I13\",\"J13\",\"K13\",\"L13\",\"M13\",\"N13\",\"O13\",\"P13\",\"Q13\",\"R13\",\"S13\",\"T13\",\"U13\",\"V13\",\"W13\"],\n *   [\"EZ5\",\"FA5\",\"EZ6\",\"FA6\",\"EZ7\",\"FA7\",\"EZ8\",\"FA8\"],\n *   [\"AAA1\",\"AAB1\",\"AAA2\",\"AAB2\",\"AAA3\",\"AAB3\"]\n * ]\n * ```\n *\n * @param {Array} a1Notations Array including A1Notations.\n * @return {Array} Array including the expanded A1Notations.\n */\nfunction expandA1Notations(a1Notations, maxRow = \"1000\", maxColumn = \"Z\") {\n  if (!Array.isArray(a1Notations) || a1Notations.length == 0) {\n    throw new Error(\"Please give a1Notations (Array).\");\n  }\n  const reg1 = new RegExp(\"^([A-Z]+)([0-9]+)$\");\n  const reg2 = new RegExp(\"^([A-Z]+)$\");\n  const reg3 = new RegExp(\"^([0-9]+)$\");\n  return a1Notations.map((e) =\u003e {\n    const a1 = e.split(\"!\");\n    const r = a1.length \u003e 1 ? a1[1] : a1[0];\n    const [r1, r2] = r.split(\":\");\n    if (!r2) return [r1];\n    let rr;\n    if (reg1.test(r1) \u0026\u0026 reg1.test(r2)) {\n      rr = [r1.toUpperCase().match(reg1), r2.toUpperCase().match(reg1)];\n    } else if (reg2.test(r1) \u0026\u0026 reg2.test(r2)) {\n      rr = [\n        [null, r1, 1],\n        [null, r2, maxRow],\n      ];\n    } else if (reg1.test(r1) \u0026\u0026 reg2.test(r2)) {\n      rr = [r1.toUpperCase().match(reg1), [null, r2, maxRow]];\n    } else if (reg2.test(r1) \u0026\u0026 reg1.test(r2)) {\n      rr = [[null, r1, maxRow], r2.toUpperCase().match(reg1)];\n    } else if (reg3.test(r1) \u0026\u0026 reg3.test(r2)) {\n      rr =\n        Number(r1) \u003e Number(r2)\n          ? [\n              [null, \"A\", r2],\n              [null, maxColumn, r1],\n            ]\n          : [\n              [null, \"A\", r1],\n              [null, maxColumn, r2],\n            ];\n    } else if (reg1.test(r1) \u0026\u0026 reg3.test(r2)) {\n      rr = [r1.toUpperCase().match(reg1), [null, maxColumn, r2]];\n    } else if (reg3.test(r1) \u0026\u0026 reg1.test(r2)) {\n      let temp = r2.toUpperCase().match(reg1);\n      rr =\n        Number(temp[2]) \u003e Number(r1)\n          ? [\n              [null, temp[1], r1],\n              [null, maxColumn, temp[2]],\n            ]\n          : [temp, [null, maxColumn, r1]];\n    } else {\n      throw new Error(`Wrong a1Notation: ${r}`);\n    }\n    const obj = {\n      startRowIndex: Number(rr[0][2]),\n      endRowIndex: rr.length == 1 ? Number(rr[0][2]) + 1 : Number(rr[1][2]) + 1,\n      startColumnIndex: columnLetterToIndex(rr[0][1]),\n      endColumnIndex:\n        rr.length == 1\n          ? columnLetterToIndex(rr[0][1]) + 1\n          : columnLetterToIndex(rr[1][1]) + 1,\n    };\n    let temp = [];\n    for (let i = obj.startRowIndex; i \u003c obj.endRowIndex; i++) {\n      for (let j = obj.startColumnIndex; j \u003c obj.endColumnIndex; j++) {\n        temp.push(columnIndexToLetter(j) + i);\n      }\n    }\n    return temp;\n  });\n}\n````\n\n\u003ca name=\"consolidatea1notations\"\u003e\u003c/a\u003e\n\n### consolidateA1Notations\n\n![](https://tanaikech.github.io/image-storage/20240416a/fig2.png)\n\nThis method is used for consolidating the scattered A1Notations.\n\nYou can see my report using this method at [https://medium.com/@tanaike/consolidate-scattered-a1notations-into-continuous-ranges-on-google-spreadsheet-using-google-apps-c9ce870dcb99](https://medium.com/@tanaike/consolidate-scattered-a1notations-into-continuous-ranges-on-google-spreadsheet-using-google-apps-c9ce870dcb99).\n\n````javascript\n/**\n * ### Description\n * This method is used for consolidating the scattered A1Notations.\n *\n * ### Sample script\n * ```\n * const a1Notations = [\"C1\", \"I1\", \"B2\", \"C2\", \"D2\", \"E2\", \"F2\", \"H2\", \"I2\", \"C3\", \"D3\", \"F3\", \"H3\", \"I3\", \"C4\", \"D4\", \"E4\", \"H4\", \"K4\", \"C5\", \"E5\", \"F5\", \"I5\", \"J5\", \"K5\", \"F6\", \"I6\", \"K6\", \"D7\", \"E7\", \"I7\", \"J7\", \"K7\", \"D8\", \"I8\", \"J8\", \"D9\", \"J9\", \"K9\", \"D10\"];\n * const res = UtlApp.consolidateA1Notations(a1Notations);\n * console.log(JSON.stringify(res));\n * ```\n *\n * Result is as follows.\n *\n * ```\n * [\"C1:C5\",\"K4:K7\",\"I5:I8\",\"D7:D10\",\"I1:I3\",\"D2:F2\",\"H2:H4\",\"J7:J9\",\"D3:D4\",\"E4:E5\",\"F5:F6\",\"B2\",\"F3\",\"J5\",\"E7\",\"K9\"]\n * ```\n *\n * @param {Array} a1Notations Scattered A1Notations.\n * @return {Array} Array including the consolidated A1Notations.\n */\nfunction consolidateA1Notations(array) {\n  return new ConsolidateA1Notations(array).run();\n}\n\n/**\n * Consolidate Scattered A1Notations into Continuous Ranges on Google Spreadsheet.\n */\nclass ConsolidateA1Notations {\n  /**\n   *\n   * @param {Array} a1Notations Scattered A1Notations.\n   */\n  constructor(a1Notations) {\n    this.a1Notations = a1Notations;\n  }\n\n  /**\n   * ### Description\n   * Main method.\n   *\n   * @returns {Array} Array including consolidated A1Notations.\n   */\n  run() {\n    return this.getConsolidateA1Notations_(this.a1Notations);\n  }\n\n  /**\n   * ### Description\n   * Processing to consolidate A1Notations.\n   *\n   * @param {Array} a1Notations Array including the inputted A1Notations.\n   * @returns {Array} Array including consolidated A1Notations.\n   */\n  getConsolidateA1Notations_(a1Notations) {\n    const expandedA1Notations = expandA1Notations(a1Notations).flat();\n    const orgGridRanges = expandedA1Notations.map((e) =\u003e\n      convA1NotationToGridRange(e, 0)\n    );\n    const gridRanges = orgGridRanges\n      .sort((a, b) =\u003e (a.startColumnIndex \u003c b.startColumnIndex ? 1 : -1))\n      .sort((a, b) =\u003e (a.startRowIndex \u003e b.startRowIndex ? 1 : -1));\n    const maxRow = Math.max(\n      ...gridRanges.map(({ endRowIndex }) =\u003e endRowIndex)\n    );\n    const maxCol = Math.max(\n      ...gridRanges.map(({ endColumnIndex }) =\u003e endColumnIndex)\n    );\n    const obj = gridRanges.reduce((o, e) =\u003e {\n      const { startRowIndex, endRowIndex, startColumnIndex, endColumnIndex } =\n        e;\n      const key = `sr@${startRowIndex}_er@${endRowIndex}_sc@${startColumnIndex}_ec@${endColumnIndex}`;\n      o[key] = e;\n      return o;\n    }, {});\n    let copiedGridranges = gridRanges.slice();\n    let copiedObj = { ...obj };\n    const res = [];\n    do {\n      const resObj = this.getMaxAreas_({\n        obj: copiedObj,\n        gridRanges: copiedGridranges,\n        maxRow,\n        maxCol,\n      });\n      res.push(resObj);\n      if (resObj.length \u003e 0) {\n        const removeKeys = resObj.flatMap(({ removeKeys }) =\u003e\n          removeKeys.flatMap(({ remove }) =\u003e remove)\n        );\n        copiedObj = Object.fromEntries(\n          Object.entries(copiedObj).filter(([k]) =\u003e !removeKeys.includes(k))\n        );\n        copiedGridranges = Object.entries(copiedObj).map(([, v]) =\u003e v);\n      }\n    } while (Object.keys(copiedObj).length \u003e 0);\n    const result = res.reduce((ar, e) =\u003e {\n      if (e.length \u003e 0) {\n        e.forEach((f) =\u003e {\n          f.removeKeys.forEach((g) =\u003e {\n            if (g.topLeft == g.bottomRight) {\n              ar.push(g.topLeft);\n            } else {\n              ar.push(`${g.topLeft}:${g.bottomRight}`);\n            }\n          });\n        });\n      }\n      return ar;\n    }, []);\n    return result;\n  }\n\n  /**\n   * ### Description\n   * Calculated the maximum rectangles from the inputted A1Notations.\n   *\n   * @param {Object} object GridRange converted from the inputted A1Notations, the numbers of maximum row and column.\n   * @returns {Object} Object including the calculated maximum rectangles.\n   */\n  getMaxAreas_(object) {\n    let { obj, gridRanges, maxRow, maxCol } = object;\n    const areas = gridRanges.map((o) =\u003e {\n      const { startRowIndex, startColumnIndex } = o;\n      const cc = [];\n      const rr = [];\n      for (let r = startRowIndex; r \u003c maxRow; r++) {\n        for (let c = startColumnIndex; c \u003c maxCol; c++) {\n          const key = `sr@${r}_er@${r + 1}_sc@${c}_ec@${c + 1}`;\n          if (!obj[key]) {\n            cc.push(c - startColumnIndex);\n            break;\n          } else if (c == maxCol - 1) {\n            cc.push(c - startColumnIndex + 1);\n            break;\n          }\n        }\n      }\n      for (let c = startColumnIndex; c \u003c maxCol; c++) {\n        for (let r = startRowIndex; r \u003c maxRow; r++) {\n          const key = `sr@${r}_er@${r + 1}_sc@${c}_ec@${c + 1}`;\n          if (!obj[key]) {\n            rr.push(r - startRowIndex);\n            break;\n          } else if (r == maxRow - 1) {\n            rr.push(r - startRowIndex + 1);\n            break;\n          }\n        }\n      }\n      const r1 = rr.reduce(\n        (oo, e) =\u003e {\n          if (e \u003c oo.temp) {\n            oo.temp = e;\n            oo.v.push(oo.temp);\n          } else {\n            oo.v.push(oo.temp);\n          }\n          return oo;\n        },\n        { temp: rr[0], v: [] }\n      );\n      const c1 = cc.reduce(\n        (oo, e) =\u003e {\n          if (e \u003c oo.temp) {\n            oo.temp = e;\n            oo.v.push(oo.temp);\n          } else {\n            oo.v.push(oo.temp);\n          }\n          return oo;\n        },\n        { temp: cc[0], v: [] }\n      );\n      const r0 = r1.v.indexOf(0);\n      const c0 = c1.v.indexOf(0);\n      if (r0 \u003e -1) {\n        r1.v.splice(r0);\n      }\n      if (c0 \u003e -1) {\n        c1.v.splice(c0);\n      }\n      const areas = [];\n      let c1v = c1.v.slice();\n      for (let x = 1; x \u003c= r1.v[0]; x++) {\n        const y = c1v.shift();\n        const key = `sr@${o.startRowIndex + x - 1}_er@${\n          o.startRowIndex + x\n        }_sc@${o.startColumnIndex + y - 1}_ec@${o.startColumnIndex + y}`;\n        obj = Object.fromEntries(Object.entries(obj).filter(([k]) =\u003e k != key));\n        areas.push({ r: x, c: y, area: x * y });\n      }\n      const maxArea = Math.max(...areas.map((e) =\u003e e.area));\n      const tempMaxAreaObj = areas.filter((e) =\u003e e.area == maxArea);\n      if (tempMaxAreaObj.length == 0) {\n        return [\n          {\n            areas: [],\n            maxAreaObj: [],\n            o,\n            removeKeys: [{ remove: [], topLeft: \"\", bottomRight: \"\" }],\n          },\n        ];\n      }\n      const maxAreaObj = [tempMaxAreaObj[0]];\n      const removeKeys = maxAreaObj.map((e) =\u003e {\n        const temp = [];\n        for (let i = o.startRowIndex; i \u003c o.startRowIndex + e.r; i++) {\n          for (let j = o.startColumnIndex; j \u003c o.startColumnIndex + e.c; j++) {\n            const key = `sr@${i}_er@${i + 1}_sc@${j}_ec@${j + 1}`;\n            temp.push(key);\n          }\n        }\n        return {\n          remove: temp,\n          topLeft: `${columnIndexToLetter(o.startColumnIndex)}${\n            o.startRowIndex + 1\n          }`,\n          bottomRight: `${columnIndexToLetter(o.startColumnIndex + e.c - 1)}${\n            o.startRowIndex + e.r\n          }`,\n        };\n      });\n      return { areas, maxAreaObj, o, removeKeys };\n    });\n    const a = Math.max(\n      ...areas.map(({ maxAreaObj }) =\u003e\n        maxAreaObj \u0026\u0026 maxAreaObj.length \u003e 0 \u0026\u0026 maxAreaObj[0]?.area\n          ? maxAreaObj[0].area\n          : -1\n      )\n    );\n    const result = areas.filter(\n      ({ maxAreaObj }) =\u003e\n        maxAreaObj \u0026\u0026\n        maxAreaObj.length \u003e 0 \u0026\u0026\n        maxAreaObj[0]?.area \u0026\u0026\n        maxAreaObj[0]?.area == a\n    );\n    return result;\n  }\n}\n````\n\n\u003ca name=\"blobtodataurl\"\u003e\u003c/a\u003e\n\n### blobToDataUrl\n\nThis method is used for converting Blob to the data URL.\n\n````javascript\n/**\n * ### Description\n * This method is used for converting Blob to the data URL.\n *\n * ### Sample script\n * ```\n * const blob = Utilities.newBlob(\"sample\", MimeType.PLAIN_TEXT);\n * const res = UtlApp.blobToDataUrl(blob);\n * console.log(res);\n * ```\n *\n * Result is as follows.\n *\n * ```\n * data:text/plain;base64,c2FtcGxl\n * ```\n *\n * @param {Blob} blob Blob\n * @return {String} Data URL converted from Blob.\n */\nfunction blobToDataUrl(blob) {\n  if (typeof blob != \"object\" || blob.toString() != \"Blob\") {\n    throw new Error(\"Please give Blob as an argument.\");\n  }\n  const mimeType = blob.getContentType();\n  if (!mimeType) {\n    throw new Error(\n      \"Given Blob has no mimeType. Please set the mimeType to Blob.\"\n    );\n  }\n  return `data:${mimeType};base64,${Utilities.base64Encode(blob.getBytes())}`;\n}\n````\n\n\u003ca name=\"snakecasetocamelcase\"\u003e\u003c/a\u003e\n\n### snake_caseToCamelCase\n\nThis method is used for converting a string of the snake case to the camel case.\n\n````javascript\n/**\n * ### Description\n * This method is used for converting a string of the snake case to the camel case.\n *\n * ### Sample script\n * ```\n * const res = UtlApp.snake_caseToCamelCase(\"sample1_sample2_sample3\", true);\n * console.log(res);\n * ```\n *\n * Result is as follows.\n *\n * ```\n * Sample1Sample2Sample3\n * ```\n *\n * @param {String} value String value of the snake case.\n * @param {Boolean} upperCaseForTopCharacter When this is true, the top character is converted to upper case. The default is false.\n * @return {String} String value converted from the snake case to the camel case.\n */\nfunction snake_caseToCamelCase(value, upperCaseForTopCharacter = false) {\n  if (!value || typeof value != \"string\") {\n    throw new Error(\"Please set string value of the snake case.\");\n  }\n  if (upperCaseForTopCharacter) {\n    value = value.replace(/^./, ([a]) =\u003e a.toUpperCase());\n  }\n  return value.replace(/_./g, ([, a]) =\u003e a.toUpperCase());\n}\n````\n\n\u003ca name=\"camelcasetosnakecase\"\u003e\u003c/a\u003e\n\n### camelCaseTosnake_case\n\nThis method is used for converting a string of the camel case to the snake case.\n\n````javascript\n/**\n * ### Description\n * This method is used for converting a string of the camel case to the snake case.\n *\n * ### Sample script\n * ```\n * const res = UtlApp.camelCaseTosnake_case(\"Sample1Sample2Sample3\");\n * console.log(res);\n * ```\n *\n * Result is as follows.\n *\n * ```\n * sample1_sample2_sample3\n * ```\n *\n * @param {String} value String value of the snake case.\n * @return {String} String value converted from the snake case to the camel case.\n */\nfunction camelCaseTosnake_case(value) {\n  if (!value || typeof value != \"string\") {\n    throw new Error(\"Please set string value of the camel case.\");\n  }\n  return value.replace(/.[A-Z]/g, ([a, b]) =\u003e `${a}_${b}`).toLocaleLowerCase();\n}\n````\n\n\u003ca name=\"createformdataobject\"\u003e\u003c/a\u003e\n\n### createFormDataObject\n\nThis method is used for creating the form data to HTTP request from an object.\n\n````javascript\n/**\n * ### Description\n * This method is used for creating the form data to HTTP request from an object.\n *\n * ### Sample script\n * ```\n * const obj = {\n *   key0: \"value0\",\n *   key1: {\n *     key1a: \"value1a\",\n *     key1b: \"value1b\",\n *   },\n *   key2: {\n *     key2a: {\n *       key2aa: \"value2aa\",\n *       key2ab: \"value2ab\",\n *     },\n *     key1b: \"value1b\",\n *   },\n *   key3: [\"ar1\", \"ar2\", \"ar3\"],\n * };\n * const res = UtlApp.createFormDataObject(obj, false);\n * console.log(res);\n * ```\n *\n * Result is as follows.\n *\n * ```\n * {\n *   \"key0\": \"value0\",\n *   \"key1[key1a]\": \"value1a\",\n *   \"key1[key1b]\": \"value1b\",\n *   \"key2[key2a][key2aa]\": \"value2aa\",\n *   \"key2[key2a][key2ab]\": \"value2ab\",\n *   \"key2[key1b]\": \"value1b\",\n *   \"key3[0]\": \"ar1\",\n *   \"key3[1]\": \"ar2\",\n *   \"key3[2]\": \"ar3\"\n * }\n * ```\n *\n * When the 2nd argument is true, the followine result is obtained.\n *\n * ```\n * key0=value0\u0026key1[key1a]=value1a\u0026key1[key1b]=value1b\u0026key2[key2a][key2aa]=value2aa\u0026key2[key2a][key2ab]=value2ab\u0026key2[key1b]=value1b\u0026key3[0]=ar1\u0026key3[1]=ar2\u0026key3[2]=ar3\n * ```\n *\n * @param {Object} object Object for converting to the form data.\n * @param {Boolean} asQueryParameters When this is true, the result is returned as the query parameter. The default is false.\n * @return {Object|String}\n */\nfunction createFormDataObject(object, asQueryParameters = false) {\n  if (!object || typeof object != \"object\") {\n    throw new Error(\"Please set an object.\");\n  }\n\n  // ref: https://stackoverflow.com/a/19101235\n  Object.flatten = function (data) {\n    var result = {};\n    function recurse(cur, prop) {\n      if (Object(cur) !== cur) {\n        result[prop] = cur;\n      } else if (Array.isArray(cur)) {\n        for (var i = 0, l = cur.length; i \u003c l; i++)\n          recurse(cur[i], prop + \"[\" + i + \"]\");\n        if (l == 0) result[prop] = [];\n      } else {\n        var isEmpty = true;\n        for (var p in cur) {\n          isEmpty = false;\n          recurse(cur[p], prop ? prop + \"___\" + p : p);\n        }\n        if (isEmpty \u0026\u0026 prop) result[prop] = {};\n      }\n    }\n    recurse(data, \"\");\n    return result;\n  };\n\n  const obj = Object.flatten(object);\n  const res = Object.entries(obj).map(([k, v]) =\u003e {\n    const [t1, ...t2] = k.split(\"___\");\n    return [`${t1}${t2.map((e) =\u003e `[${e}]`).join(\"\")}`, v];\n  });\n  if (asQueryParameters) {\n    return res.map(([k, v]) =\u003e `${k}=${encodeURIComponent(v)}`).join(\"\u0026\");\n  }\n  return Object.fromEntries(res);\n}\n````\n\n---\n\n# If you want to use only one method in the above methods without using a library\n\nIf you want to use only one method in the above methods without a library, you can do this simply.\n\nIn that case, please copy and paste one of the methods from the above scripts. At this time, there are several methods using the other methods. Please copy and paste all dependence methods.\n\nIf you want to run the method directly, please remove the identification of the library `UtlApp`. By this, the script is run.\n\nFor example, when you want to directly use the following method `is2DimensionalArray` without the library, please copy and paste the following script to the script editor.\n\n````javascript\n/**\n * ### Description\n * When the inputted array is 2 dimensional array, true is returned.\n *\n * ### Sample script\n * ```\n * const array1 = [\"\", [1, 2, 3], [1, 2, 3], [1, 2, 3]];\n * const res1 = UtlApp.is2DimensionalArray(array1); // false\n *\n * const array2 = [[1, 2, 3], [1, 2], [1]];\n * const res2 = UtlApp.is2DimensionalArray(array2); // true\n * ```\n *\n * @param {Array} array 2 dimensional array.\n * @return {Boolean} When the inputted array is 2 dimensional array, true is returned.\n */\nfunction is2DimensionalArray(array) {\n  return array.every((r) =\u003e Array.isArray(r));\n}\n````\n\nWhen you run this method, please use it with the following script.\n\n```javascript\nfunction sample() {\n  const array1 = [\"\", [1, 2, 3], [1, 2, 3], [1, 2, 3]];\n  const res1 = is2DimensionalArray(array1); // false\n}\n```\n\n# Contribution\n\nI believe that these methods will help to develop the applications created by Google Apps Script. So, I would like to grow this. If you want to add more methods, I welcome your contributions. At that time, please do \"pull request\" or email me ( tanaike@hotmail.com ). By testing your method, I would like to add it. Your name is also added as a contributor.\n\n**The policy of these methods is not to use the scopes.** So, when you add your method, **please confirm that your proposed method uses no scopes**. And also, please check your JSDoc.\n\n---\n\n\u003ca name=\"licence\"\u003e\u003c/a\u003e\n\n# Licence\n\n[MIT](LICENCE)\n\n\u003ca name=\"author\"\u003e\u003c/a\u003e\n\n# Author\n\n[Tanaike](https://tanaikech.github.io/about/)\n\n[Donate](https://tanaikech.github.io/donate/)\n\n\u003ca name=\"updatehistory\"\u003e\u003c/a\u003e\n\n# Update History\n\n- v1.0.0 (May 13, 2023)\n\n  1. Initial release.\n\n- v1.0.1 (November 12, 2023)\n\n  1. From [this report](https://github.com/tanaikech/UtlApp/issues/1) by [Max-Makhrov](https://github.com/Max-Makhrov), a bug of `convA1NotationToGridRange` was removed.\n\n- v1.0.2 (January 25, 2024)\n\n  1. 2 methods of [dotProduct](#dotproduct) and [cosineSimilarity](#cosinesimilarity) were added.\n\n- v1.0.3 (February 5, 2024)\n\n  1. Methods of [addQueryParameters](#addqueryparameters) was updated.\n\n- v1.0.4 (April 13, 2024)\n\n  1. Updated the methods of [convGridRangeToA1Notation](#convgridrangetoa1Notation). When the sheet name is not given, only the A1Notation is returned.\n\n- v1.0.5 (April 16, 2024)\n\n  1. Methods of [consolidateA1Notations](#consolidatea1notations) was added. When this method is used, the scattered A1Notations can be consolidated.\n\n- v1.0.6 (May 4, 2024)\n\n  1. A method of [blobToDataUrl](#blobtodataurl) was added. When this method is used, the Blob data can be converted to the data URL.\n\n- v1.0.7 (September 4, 2024)\n  1. Following 3 methods were added.\n  - [snake_caseToCamelCase](#snakecasetocamelcase): This method is used for converting a string of the snake case to the camel case.\n  - [camelCaseTosnake_case](#camelcasetosnakecase): This method is used for converting a string of the camel case to the snake case.\n  - [createFormDataObject](#createformdataobject): This method is used for creating the form data to HTTP request from an object.\n\n[TOP](#top)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftanaikech%2FUtlApp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftanaikech%2FUtlApp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftanaikech%2FUtlApp/lists"}