I have a JavaScript array of objects which looks like
var myarr = [
{'xx':'2023-01-01,,1'},
{'ss':'2023-01-01,2,1.2'},
{'dd':'2023-01-01,4,'},
{'rr':'2023-01-01,,'},
{'ff':'2023-01-01,,'},
{'gg':'2023-01-01,,'}
];
The array is actually much bigger than that, but I have cut it down for testing purposes, some of my arrays are thousands of lines long
Each object contains a date and two comma-separated values, although I have some rows which contain 3 or 4 comma separate values
What I need to do, is if any blank comma-separated value is found on any row then get the previous comma separated value from that position to a maximum of 2 times going back, although I may need to change that to a bigger number in the future
So with my example, I would get the following output
var myarr = [
{'xx':'2023-01-01,,1.6'},
{'ss':'2023-01-01,2,1.2'},
{'dd':'2023-01-01,4,1.2'},
{'rr':'2023-01-01,4,1.2'},
{'ff':'2023-01-01,4,'},
{'gg':'2023-01-01,,'}
];
I have tried to solve this with
var myarr = [
{'xx':'2023-01-01,,1'},
{'ss':'2023-01-01,2,1.2'},
{'dd':'2023-01-01,4,'},
{'rr':'2023-01-01,,'},
{'ff':'2023-01-01,,'},
{'gg':'2023-01-01,,'}
];
var maxAttempts = 3;
for (var i = 0; i < myarr.length; i++) {
var obj = myarr[i];
var values = Object.values(obj)[0].split(",");
var date = values[0];
var value1 = values[1];
var value2 = values[2];
for (var j = 1; j <= maxAttempts; j++) {
if (!value1) {
value1 = (myarr[i-j] && Object.values(myarr[i-j])[0].split(",")[1]) || " ";
}
if (!value2) {
value2 = (myarr[i-j] && Object.values(myarr[i-j])[0].split(",")[2]) || " ";
}
if (value1 && value2) {
break;
}
}
console.log(date, value1, value2);
for (var k = 3; k < values.length; k++) {
var value = values[k];
console.log(value);
}
}
but it doesn't seem to provide the expected output.
Can someone help me with what might be wrong?
Maybe you can use something like this.
const myarr = [
{ "xx": "2023-01-01,,1" },
{ "ss": "2023-01-01,2,1.2" },
{ "dd": "2023-01-01,4," },
{ "rr": "2023-01-01,," },
{ "ff": "2023-01-01,," },
{ "gg": "2023-01-01,," }
]
function fillInBlanks(arr, maxLookBack) {
return arr.map((obj, index) => {
const key = Object.keys(obj)[0]
const value = Object.values(obj)[0]
.split(",")
.map((x, n) => {
if (x === "" && index > 0) {
for (let i = index - 1; i >= Math.max(0, index - maxLookBack); --i) {
const prev = Object.values(arr[i])[0].split(",")
if (prev[n] !== "") return prev[n]
}
} else return x
})
return Object.fromEntries([
[key, value.join(",")]
])
})
}
fillInBlanks(myarr, 2).forEach(x => console.log(x))
Here's my attempt. This will also work with any number of values per row.
const maxAttempts = 2;
myarr.reduce((modifiedAccumulation, currentObject, index) => {
const [key, csv] = Object.entries(currentObject)[0];
const splitCsv = csv.split(",");
const modifiedCsv = splitCsv
.reduce((fixedArray, currentElement, csvPos) => {
let numberToUse =
currentElement === ""
? myarr
.slice(Math.max(index - maxAttempts, 0), index)
.reduceRight((proposedNum, currentPastObj) => {
if (proposedNum !== "") return proposedNum;
let candidate =
Object.entries(currentPastObj)[0][1].split(",")[csvPos];
return candidate !== "" ? candidate : "";
}, "")
: currentElement;
return [...fixedArray, numberToUse];
}, [])
.join(",");
return [...modifiedAccumulation, { [key]: modifiedCsv }];
}, []);
This approach creates a 'window' array containing the last few entries, which is used to look up prior column values.
const myarr = [{"xx":"2023-01-01,,1"},{"ss":"2023-01-01,2,1.2"},{"dd":"2023-01-01,4,"},{"rr":"2023-01-01,,"},{"ff":"2023-01-01,,"},{"gg":"2023-01-01,,"}]
const windowSize = 2
const w = [], r =
myarr.map(e=>Object.entries(e).flatMap(([k,v])=>[k,...v.split(',')]))
.map(a=>(
w.unshift(a) > windowSize+1 && w.pop(),
a.map((_,i)=>w.find(x=>x[i])?.[i])
)).map(([k,...v])=>[k,v.join()]
).map(i=>Object.fromEntries([i]))
console.log(r)
I have the following url string:
"&data=true&filterList=include|value|Location|Region|Europe|Asia;include|value|Time|Current+Month+Flag|1;include|value|Employee|Employee+Type|Employee;&decimals=5;"
I ahve tried to write a js function that will delete sections of the url string depending on the delete and key variables, for example:
let key = "Region"
let delete = "Europe"
Should result in the url string changing to the following:
"&data=true&filterList=include|value|Location|Region|Asia;include|value|Time|Current+Month+Flag|1;include|value|Employee|Employee+Type|Employee;&decimals=5;"
However if the url was :
"&data=true&filterList=include|value|Location|Region|Asia;include|value|Time|Current+Month+Flag|1;include|value|Employee|Employee+Type|Employee;&decimals=5;"
And the key and delete variables were:
let key = "Region"
let delete = "Asia"
The url would be changed to:
"&data=true&filterList=include|value|Time|Current+Month+Flag|1;include|value|Employee|Employee+Type|Employee;&decimals=5;"
The function should look at how many values in the url exist after the specified key, up until the next ";"
If the only value is the one specified in the delete variable it should delete the whole section containing the key and value starting from include to the next ";"
If the value is one of multiple then it should only delete the value
//The filterList sections are formatted as so:
include | value | Subject Area | Table | Filters... (can have multiple filters, each seperated by a pipe ("|")
//IMPORTANT: the first 2 values i.e.: include | value | are always the same and always followed by a subject are that can be anything. Therefore the bits I care about start after the third |
//I have the following function:
function deleteUrlSection(url, key, deleteValue) {
const sections = url.split(';');
let updatedSections = [];
for (let i = 0; i < sections.length; i++) {
let section = sections[i];
let keyIndex = section.indexOf(key);
if (keyIndex !== -1) {
let values = section.substring(keyIndex).split('|');
let updatedValues = [];
for (let j = 0; j < values.length; j++) {
if (values[j] !== deleteValue) {
updatedValues.push(values[j]);
}
}
if (updatedValues.length > 0) {
updatedSections.push(updatedValues.join('|'));
}
} else {
updatedSections.push(section);
}
}
return updatedSections.join(';');
}
This function works fine for the following example only:
URL = "&data=true&filterList=include|value|Location|Region|Europe|Asia;include|value|Time|Current+Month+Flag|1;include|value|Employee|Employee+Type|Employee;&decimals=5;"
let key = "Region"
let delete = "Europe"
Result:
'"&data=true&filterList=include|value|Location|Region|Asia;include|value|Time|Current+Month+Flag|1;include|value|Employee|Employee+Type|Employee;&decimals=5;"`
However if I try with the url set to:
'"&data=true&filterList=include|value|Location|Region|Asia;include|value|Time|Current+Month+Flag|1;include|value|Employee|Employee+Type|Employee;&decimals=5;"`
and variables set to:
let key = "Region"
let delete = "Asia"
I get:
&cdata=true&filterList=include|value|Location|Region;include|value|Time|Current+Month+Flag|1;include|value|Employee|Employee+Type|Employee;&decimals=5;
include|value|Location|Region should be deleted...
function deleteUrlSection(url, key, deleteValue) {
const sections = url.split(';');
let updatedSections = [];
for (let i = 0; i < sections.length; i++) {
let section = sections[i];
let keyIndex = section.indexOf(key);
if (keyIndex !== -1) {
let values = section.substring(keyIndex).split('|');
let updatedValues = [];
for (let j = 0; j < values.length; j++) {
if (values[j] !== deleteValue) {
updatedValues.push(values[j]);
}
}
if (updatedValues.length > 0) {
updatedSections.push(updatedValues.join('|'));
}
} else {
updatedSections.push(section);
}
}
return updatedSections.join(';');
}
//test 1
let url = "&data=true&filterList=include|value|Location|Region|Europe|Asia;include|value|Time|Current+Month+Flag|1;include|value|Employee|Employee+Type|Employee;&decimals=5;";
let key = "Region";
let deleteV = "Asia";
console.log("New Url ", "", deleteUrlSection(url, key, deleteV));
//expected output
//&data=true&filterList=include|value|Location|Region|Europe;include|value|Time|Current+Month+Flag|1;include|value|Employee|Employee+Type|Employee;&decimals=5;
//test 2
let url2 = "&data=true&filterList=include|value|Location|Region|Europe;include|value|Time|Current+Month+Flag|1;include|value|Employee|Employee+Type|Employee;&decimals=5;";
let key2 = "Region";
let deleteV2 = "Europe";
console.log("New Url2 ", "", deleteUrlSection(url2, key2, deleteV2));
//expected output
//&data=true&filterList=include|value|Time|Current+Month+Flag|1;include|value|Employee|Employee+Type|Employee;&decimals=5;
//test 3
let url3 = "&data=true&filterList=include|value|Location|Region|Europe|Asia;include|value|Time|Current+Month+Flag|1;include|value|Employee|Employee+Type|Employee;&decimals=5;";
let key3 = "Current+Month+Flag";
let deleteV3 = "1";
console.log("New Url3 ", "", deleteUrlSection(url, key3, deleteV3));
//expected output
//&data=true&filterList=include|value|Location|Region|Europe|Asia;include|value|Time|Current+Month+Flag|1;&decimals=5;
I sorted the elements and comparing the first and last string to check the common prefixes. It works for most of the cases, but not for the input ["dog","racecar","car"]. The expected output is "", but what I'm getting is "c" (The "r" in "car" and "r" in "racecar"). I can tell the code to remove the last char, but this will break the other cases such as ["car", "car", "car"]. Not sure what am I missing. Any insights would help me improve.
Thanks
var longestCommonPrefix = function(strs) {
let count=0
const sortedString = strs.sort()
const firstString = sortedString[0]
const lastString = sortedString[sortedString.length-1]
for(let i=0; i< firstString.length; i++) {
if(firstString.charAt(i) === lastString.charAt(i)) {
count++
}
}
console.log(firstString.substring(0, count))
};
longestCommonPrefix(
["dog","racecar","car"])
You need to break out of the loop as soon as a match is not found. Otherwise, for example, ra and ca match on the second index, the a - which is undesirable.
var longestCommonPrefix = function(strs) {
let count = 0
const sortedString = strs.sort()
const firstString = sortedString[0]
const lastString = sortedString[sortedString.length - 1]
for (let i = 0; i < firstString.length; i++) {
if (firstString.charAt(i) === lastString.charAt(i)) {
count++
} else {
break;
}
}
console.log(firstString.substring(0, count))
};
longestCommonPrefix(
["dog", "racecar", "car"])
or, refactored a bit
const longestCommonPrefix = (strs) => {
strs.sort();
const firstString = strs[0];
const lastString = strs[strs.length - 1];
let prefixSoFar = '';
for (let i = 0; i < firstString.length; i++) {
if (firstString[i] === lastString[i]) {
prefixSoFar += firstString[i];
} else {
return prefixSoFar;
}
}
return prefixSoFar;
};
console.log(longestCommonPrefix(["dog", "racecar", "car"]));
have any way can find a match index in array[2D] faster?
I know compare 1 by 1 can make this ok, but i don't wanted to.
I tried this one, but it only can return -1
// mainsheetvalues is array[1D],
[1,2,3,4,5]
// AsheetValues is array [2D]
[
[1,2,3,4,5],
[6,7,8,9,0]
]
Logger.log(mainsheetvalues.indexOf(AsheetValues))
As per this answer, we cannot compare two arrays directly. You need to generate some custom logic for the comparison. I have added a logic below. Hope this helps.
const AsheetValues = [
[1,2,3,4,5],
[6,7,8,9,0]
]
const mainsheetvalues = [1,2,3,4,5];
const isIncluded = (parentArr, childArr) => {
let isMatch = true;
for(let parentLoopIndex = 0; parentLoopIndex < parentArr.length; parentLoopIndex++) {
if (parentArr[parentLoopIndex].length != childArr.length)
isMatch = false;
for (var i = 0; i < parentArr[parentLoopIndex].length; i++) {
if (parentArr[parentLoopIndex][i] != childArr[i]) {
isMatch = false;
}
}
if (isMatch) {
parentLoopIndex = parentArr.length;
}
}
return isMatch;
}
console.log(isIncluded(AsheetValues, mainsheetvalues));
So there's a JSON Object, it has a list of arrays with objects in it, like AssortmentID and AssortmentParentID with a Name, AssortimentID's with an AssortmentParentID of 000-000-000 are folders, the AssortmentID's with the AssortmentParentID of another AssortmentID are the children of that folder.
How do I output the folders first, and then the children in each folder(parent).
Example:
The HTTP Request:
const url = "\url";
async function getAssortList() {
const response = await fetch(url);
const data = response.json();
const { Assortments } = data;
for (let i = 0; i < Assortments.length; i++) {
const assortItem = Assortments[i];
...(where I'm stuck)...
/////////////////////////////////////
The JSON Response:
Assortments: [
{
"AssortmentID": 123-123-123-123,
"AssortmentParentID": 000-000-000
"Name": "I am a parent"
},
{
"AssortmentID": 111-111-111-111,
"AssortmentParentID": 123-123-123-123,
"Name": "I am a kid"
}
Maybe this will help you.
Assortments.reduce((acc, curr) => {
let parent = curr["AssortmentParentID"];
if(acc[parent]){
acc[parent].push(curr)
}
else{
acc[parent] = [curr]
}
return acc;
}, {})
try below code in javascript
Assortments.sort(
function (a, b) {
var x = a.AssortmentParentID.toLowerCase();
var y = b.AssortmentParentID.toLowerCase();
if (x < y) {return -1;}
if (x > y) {return 1;}
return 0;
})
by this you will have sorted array on AssortmentParentID
Kind of, solved it myself. Still have things to do.
const parents = []; // created a new array that I'll push objects into later.
for (let i = 0; i < Assortments.length; i++) {
if (Assortments[i].AssortimentParentID == "000-000-000") {
parents.push(Assortments[i]);
}
}
for (let i = 0; i < parents.length; i++) {
parents.sort((a, b) => a.Name.localeCompare(b.Name)); // sort them alphabetically
}
If someone can show me how to do the same thing, using filter/find/etc., it would be great. Thank you!
UPDATE:
Managed to sort the parents, output them in DOM, here's the code:
for (let x in parents) {
parents[x].isFolder === false ? parents.push( parents.splice(x,1)[0] ) : 0;
}
let assortItem = parents[i].Name;
let subnavList = document.createElement("li");
let subnavLink = document.createElement("a");
subnavList.classList.add("subnav-list");
subnavList.appendChild(subnavLink);
subnavLink.classList.add("subnav-link");
subnavLink.innerHTML += assortItem;
item.appendChild(subnavList);