read_input/util.js (275 lines of code) (raw):
/**
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Storage } from "@google-cloud/storage";
import fs from "fs";
import path from "path";
import yaml from "js-yaml";
export {
cleanKey,
lower,
cleanRes,
rmBracket,
isValue,
sepArray,
sepKeyValPairs,
lowerObj,
mergeObjArray,
mergeAddon,
createYaml,
uploadToGcs,
nestObject,
writeFile,
inverseObj,
underscoreRes,
toBool,
toNumber,
groupAddon,
customSort,
};
function cleanKey(str) {
if (!str) str = "";
return lower(sepArray(str, ":").join("."));
}
function lower(str) {
if (!str) str = "";
return rpSpaces(rmBracket(str)).toLowerCase();
}
function underscoreRes(str) {
if (!str) str = "";
return str.trim().replace(/\./g, "_").replace(/-/g, "_").toLowerCase();
}
function rpSpaces(str) {
if (!str) str = "";
return str.trim().replace(/\s+/g, "_");
}
function cleanRes(str) {
if (!str) str = "";
return str.trim().replace(/\./g, "-").replace(/_/g, "-").toLowerCase();
}
function trimQuotes(str) {
if (!str) return str;
return str.replace(/^['"]+|['"]+$/g, "");
}
function rmBracket(str, bracket = "()") {
if (!str) str = "";
switch (bracket) {
case "()":
return str.replace(/\s*\(.*\)/, "").trim();
case "[]":
return str.replace(/\s*\[.*\]/, "").trim();
case "{}":
return str.replace(/\s*\{.*\}/, "").trim();
default:
return str;
}
}
function isValue(str, value) {
return lower(str) === value ? true : false;
}
function sepArray(str, sep = ",") {
if (!str) str = "";
str = String(str);
return str
.split(sep)
.map((value) => value.trim())
.filter((value) => value);
}
function writeFile(filePath, data) {
var dir = path.dirname(filePath);
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
}
fs.writeFileSync(filePath, data);
}
function toNumber(val) {
let newVal = String(val).trim();
if (!isNaN(newVal)) {
return Number(newVal);
}
return val;
}
function toBool(val) {
let newVal = String(val).trim().toLowerCase();
if (newVal === "true") {
return true;
} else if (newVal === "false") {
return false;
}
return val;
}
function convertType(str) {
if (!isNaN(str)) {
return Number(str);
} else if (str === "true") {
return true;
} else if (str === "false") {
return false;
} else if (str === '""' || str === "''") {
return "";
}
return trimQuotes(str);
}
function sepKeyValPairs(str, sep = ";", forceVal = false, data = {}) {
if (!str) str = "";
str = String(str);
let strArray = sepArray(rmBracket(str), sep);
strArray.forEach((line) => {
let keyVal = sepArray(line, ":");
let key = keyVal[0];
let val = convertType(keyVal[1]);
if (forceVal && (val === null || val === undefined)) {
if (forceVal === "key") {
val = val || key || "";
} else {
val = val || "";
}
}
if (Array.isArray(data)) {
let d = {};
d[key] = val;
data.push(d);
} else if (typeof data === "object" && data !== null) {
data[key] = val;
}
});
return data;
}
function inverseObj(obj) {
var invObj = {};
for (var key in obj) {
invObj[obj[key]] = key;
}
return invObj;
}
function lowerObj(obj, onKey = false, onValue = false) {
const lowerCaseObj = Object.keys(obj).reduce((acc, key) => {
let modifyKey = key;
let modifyValue = obj[key];
if (onKey) modifyKey = key.toLowerCase();
if (onValue)
modifyValue =
typeof obj[key] === "string" ? obj[key].toLowerCase() : obj[key];
acc[modifyKey] = modifyValue;
return acc;
}, {});
return lowerCaseObj;
}
function mergeObjArray(arr1, arr2, name) {
return [...arr1, ...arr2].reduce((acc, obj) => {
if (acc[obj[name]]) {
acc[obj[name]] = { ...acc[obj[name]], ...obj }; // Merge properties
} else {
acc[obj[name]] = obj;
}
return acc;
}, {});
}
function mergeAddon(data, subdata, dataMergeKey, subdataMergeKey, addonKey) {
const result = data.map((item) => {
const addon = subdata
.filter((subItem) => subItem[subdataMergeKey] === item[dataMergeKey])
.map((subItem) => {
let copyItem = { ...subItem };
delete copyItem[subdataMergeKey];
return copyItem;
});
const mergeData = { ...item };
mergeData[addonKey] = addon;
return mergeData;
});
return result;
}
function groupAddon(dataArray, groupkey, arrayKey) {
const result = dataArray.reduce((acc, obj) => {
if (!acc[obj[groupkey]]) {
let myObj = { ...obj };
delete myObj[arrayKey];
acc[obj[groupkey]] = myObj;
acc[obj[groupkey]][arrayKey] = [];
}
if (obj[arrayKey]) {
let myAdd = { ...obj[arrayKey] };
acc[obj[groupkey]][arrayKey].push(myAdd);
}
return acc;
}, {});
return result;
}
function createYaml(data) {
const yamlString = yaml.dump(data);
return yamlString;
}
async function uploadToGcs(bucket_name, object_name, contents) {
const storage = new Storage();
try {
await storage
.bucket(bucket_name)
.file(object_name)
.save(contents);
console.log(
`Successfully pushed file ${object_name} to GCS bucket ${bucket_name}`
);
} catch (error) {
console.log("Failed to push file to GCS bucket: " + error);
}
}
function nestObject(flatObject, delimiter = ".") {
const nestedObject = {};
for (const flatKey in flatObject) {
// splits by delimiter escapes delimiter in quotes
var re = new RegExp(
String.raw`(?:[^${delimiter}"']+|['"][^'"]*["'])+`,
"g"
);
// const keys = flatKey.match(/(?:[^\."']+|['"][^'"]*["'])+/g);
const keys = flatKey.match(re);
let currentLevel = nestedObject;
let lastKeyIndex = keys.length - 1;
for (let i = 0; i < keys.length; i++) {
// trim "" quoted key
const key = trimQuotes(keys[i]);
// matches [0][1][2] in key
const arrayMatch = key.match(/^(.+?)\[(.+)\]$/);
if (arrayMatch) {
const arrayKey = arrayMatch[1];
const arrayIndices = arrayMatch[2].split("][").map(Number);
currentLevel[arrayKey] = currentLevel[arrayKey] || [];
let currentArray = currentLevel[arrayKey];
for (let j in arrayIndices) {
var arrayIndex = arrayIndices[j];
while (currentArray.length < arrayIndex) {
currentArray.push(null);
}
// skip last nested array
if (j < arrayIndices.length - 1) {
currentArray[arrayIndex] = currentArray[arrayIndex] || [];
currentArray = currentArray[arrayIndex];
}
}
// assign data at last nested array
if (i === lastKeyIndex) {
currentArray[arrayIndex] = flatObject[flatKey];
} else {
// If NOT last key in path, ensure array element is an object
currentArray[arrayIndex] = currentArray[arrayIndex] || {};
currentLevel = currentArray[arrayIndex];
}
} else {
// Handle object keys normally
if (i === lastKeyIndex) {
currentLevel[key] = flatObject[flatKey];
} else {
currentLevel[key] = currentLevel[key] || {};
currentLevel = currentLevel[key];
}
}
}
}
return nestedObject;
}
function customSort(arr, orderMap) {
arr.sort((a, b) => {
const indexA = orderMap[a];
const indexB = orderMap[b];
// Handle cases where elements are not in the custom order
if (indexA === undefined && indexB === undefined) {
// If both elements are not in the orderlist, keep as it is
return 0;
} else if (indexA === undefined) {
// If a is not in the orderlist, place it after b
return 1;
} else if (indexB === undefined) {
// If b is not in the orderlist, place it after a
return -1;
} else {
// If both elements are in the order, sort by their indices
return indexA - indexB;
}
});
return arr;
}