Array/Object Sorting - javascript

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);

Related

Grouping arrays based on duration string

I need to group elements in different containers based on overlapping periods. Let's say we have the following array:
const periods = ['1-4', '1-7', '1-4', '1-8', '1-8', '5-8', '9-12', '9-12'];
I need it to turn into:
[['1-4', '5-8', '9-12'], ['1-7', '9-12'], ['1-4'], ['1-8'], ['1-8']]
Basically, elements get pushed into a "row". If the element's period overlaps with another inside the current row, it would create another one.
In this example '1-4', '5-8' and '9-12' are grouped because their periods do not overlap.
I'm doing this with DOM elements, I used arrays in an attempt to simplify things. Any ideas?
Mayby this code can help you to solve your case.
const periods = ["1-4", "1-7", "1-4", "1-8", "1-8", "5-8", "9-12", "9-12"];
const groupPeriods = (periods) => {
const groups = {};
const listOfPeriodsGlobal = [...periods];
for (let i = 0; i < periods.length; i++) {
const joinPeriods = [];
const listOfPeriods = [...listOfPeriodsGlobal];
for (let j = 0; j < listOfPeriods.length; j++) {
if (isInRange(listOfPeriods[j], joinPeriods)) {
joinPeriods.push(listOfPeriods[j]);
listOfPeriodsGlobal.splice(j, 1);
}
}
groups[i] = joinPeriods;
}
return groups;
};
const isInRange = (period, joinPeriods) => {
let respose = true;
for (let i = 0; i < joinPeriods.length; i++) {
const range = joinPeriods[i];
const rangesplited = range.split("-");
const periodsplited = period.split("-");
if (
(parseInt(periodsplited[0]) >= parseInt(rangesplited[0]) &&
parseInt(periodsplited[0]) <= parseInt(rangesplited[1])) ||
(parseInt(periodsplited[1]) >= parseInt(rangesplited[0]) &&
parseInt(periodsplited[1]) <= parseInt(rangesplited[1]))
) {
respose = false;
} else {
response = true;
}
}
return respose;
};
console.log(groupPeriods(periods));
You could find the group with checking the last value against the start value.
const
periods = ['1-4', '1-7', '1-4', '1-8', '1-8', '5-8', '9-12', '9-12'],
groups = periods.reduce((r, p) => {
const
start = +p.split('-', 1),
t = r.find(a => +a.at(-1).split('-')[1] < start);
if (t) t.push(p);
else r.push([p]);
return r;
}, []);
console.log(groups);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Most efficient way to split string into tree structure

I have a dynamic array of the string. Each string represents the node of the tree. If any string has "_", I want to split into each node and store it in another array.
For. e.g.
'0_2_0_0' needs to split into "0", "0_2", "0_2_0", & "0_2_0_0" then store it in the new array.
I've achieved it by using the multiple for loops. I do not think this is the most efficient way of doing it.
let visibleTreeNodes = [];
const treeData = ['0_0', '0_1', '0_2_0_0', '1'];
for (let i = 0; i < treeData.length; i++) {
if (treeData[i].includes('_')) {
const nodesArray = treeData[i].split('_');
for (let i = 0; i < nodesArray.length; i++) {
let node = nodesArray[0];
for (let j = 0; j <= i; j++) {
if (j !== 0) {
node = node + '_' + nodesArray[j];
}
}
if (visibleTreeNodes.indexOf(node) === -1) {
visibleTreeNodes.push(node)
}
}
} else if (visibleTreeNodes.indexOf(treeData[i]) === -1) {
visibleTreeNodes.push(treeData[i])
}
}
console.log(treeData);
console.log(visibleTreeNodes);
All that is left for me is to explain (and remember) the part of building the tree by #Nina Scholz. I'll basically rename the variables and use familiar syntax.
// data can be directories lists
const data = ['0_0', '0_1', '0_2_0_0', '1']
// the classic group by using reduce
var result = data.reduce(function(agg, item) {
// buliding tree path incrementally
var pointer = agg;
item.split('_').forEach(function(part) {
pointer[part] = pointer[part] || {}
pointer = pointer[part];
});
return agg;
}, {});
// result is classic object tree
console.log(result);
// iterate to print desired output:
function iterate(tree, parent) {
Object.keys(tree).forEach(function(key) {
var value = tree[key];
var full = (parent ? parent + "_" : '') + key
console.log(full)
if (typeof value === 'object') {
iterate(value, full)
}
})
}
iterate(result)
.as-console-wrapper {max-height: 100% !important}
You could build a tree first and then get the pathes from the keys.
const
getFlat = object => Object.keys(object).flatMap(k => [
k,
...getFlat(object[k]).map(p => `${k}_${p}`)
]),
data = ['0_0', '0_1', '0_2_0_0', '1'],
result = getFlat(data.reduce((t, s) => {
s.split('_').reduce((o, k) => o[k] ??= {}, t);
return t;
}, {}));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I tried to simplify your equation. I reached this below snippet. It has a forEach and then a while loop inside forEach for every input string.
let visibleTreeNodes = [];
const treeData = ['0_0', '0_1', '0_2_0_0', '1'];
treeData.forEach(x => {
const arr = x.split("_");
let i = 0;
let node = arr[0];
while (i < arr.length) {
if (!visibleTreeNodes.includes(node)) {
visibleTreeNodes.push(node);
}
node = [node, arr[i + 1]].join("_");
i++;
}
});
console.log(treeData);
console.log(visibleTreeNodes);
You are free to figure out any better solution.
Thanks
A "one-liner":
const treeData = ['0_0', '0_1', '0_2_0_0', '1'];
const visibleTreeNodes = [...new Set(treeData.flatMap(
s=>s.split('_').reduce((a, si, i)=> [...a, a[i-1]+'_'+si])
))];// Set used to remove duplicates
console.log(visibleTreeNodes)
though efficiency must be tested - a more concise solution doesn't automatically result in a shorter run time

strings from array contain common substring javascript

Here is my code:
I want to iterate through each pair of elements of the arrays, and if the strings in each pair contain one or more common substrings, console.log(true), otherwise console.log(false).
So, output should be true, false, because "first" and "criss" have common substrings ("r", "i", "s")
Here is my code for now;
const a = ["first", "hi"];
const b = ["criss", "student"];
function commonSubstring(a, b) {
a.forEach(a1 =>
b.forEach(a2 => {
for (i = 0; i < a1.length; i++) {
if (a1.charAt(i) == a2.charAt(i)) {
console.log(true");
}
}
})
);
}
commonSubstring(a, b);
Thanks for answers in advance!
You could take a Set and check if a character is common.
function common(a, b) {
return [...a].some(Set.prototype.has, new Set(b));
}
var a = ["first", "hi"],
b = ["criss", "student"],
result = a.map((v, i) => common(v, b[i]));
console.log(result);
const a = ["first", "hi"];
const b = ["criss", "student"];
function commonSubstring(a, b) {
let result = [];
a.forEach(a1 => {
let found = false;
b.forEach(a2 => {
for (i = 0; i < a1.length; i++) {
if (a1.charAt(i) == a2.charAt(i)) {
//console.log(true);
found = true;
}
}
})
result.push(found);
//console.log(found)
});
return result.join(',');
}
console.log(commonSubstring(a, b));

How to Splice in a javascript array based on property?

I am getting an array of data in Angularjs Grid and I need to delete all the rows which has same CustCountry
ex - My Customer Array looks like
Customer[0]={ CustId:101 ,CustName:"John",CustCountry:"NewZealand" };
Customer[1]={ CustId:102 ,CustName:"Mike",CustCountry:"Australia" };
Customer[2]={ CustId:103 ,CustName:"Dunk",CustCountry:"NewZealand" };
Customer[3]={ CustId:104 ,CustName:"Alan",CustCountry:"NewZealand" };
So , in the Grid I need to delete all three records if CustomerCountry is NewZealand
I am using splice method and let me know how can I use by splicing through CustomerCountry
$scope.remove=function(CustCountry)
{
$scope.Customer.splice(index,1);
}
If you're okay with getting a copy back, this is a perfect use case for .filter:
Customer = [
{ CustId:101 ,CustName:"John",CustCountry:"NewZealand" },
{ CustId:102 ,CustName:"Mike",CustCountry:"Australia" },
{ CustId:103 ,CustName:"Dunk",CustCountry:"NewZealand" },
{ CustId:104 ,CustName:"Alan",CustCountry:"NewZealand" },
]
console.log(Customer.filter(cust => cust.CustCountry !== "NewZealand"));
if you have one specific country in mind then just use .filter()
$scope.Customer = $scope.Customer.filter(obj => obj.CustCountry !== "SpecificCountry")
If you want to delete all objects with duplicate countries then, referring to Remove duplicate values from JS array, this is what you can do:
var removeDuplicateCountries = function(arr){
var dupStore = {};
for (var x= 0; x < arr.length; x++){
if (arr[x].CustCountry in dupStore){
dupStore[arr[x].CustCountry] = false;
} else {
dupStore[arr[x].CustCountry] = true;
}
}
var newarr = [];
for (var x= 0; x < arr.length; x++){
if (dupStore[arr[x].CustCountry]){
newarr.push(arr[x]);
}
}
return arr;
};
$scope.Customer = removeDuplicateCountries($scope.Customer);
Or incorporating the .filter() method
var removeDuplicateCountries = function(arr){
var dupStore = {};
var newarr = arr;
for (var x= 0; x < arr.length; x++){
if (arr[x].CustCountry in dupStore){
newarr = newarr.filter(obj => obj.CustCountry !== arr[x].CustCountry);
} else {
dupStore[arr[x].CustCountry] = true;
}
}
return newarr;
};
$scope.Customer = removeDuplicateCountries($scope.Customer);
if there are many duplicate countries then use the way without .filter()

Javascript alphabetical grouping

I have a json array of objects that look like this: {id:'the id', name:'the name'}; and I need to loop over the array and group each object alphabetically by it's name attribute. Is there a way to do this without using a switch / if statement with every letter in it?
What I don't want to do is something like this:
if(data[i].name..slice(0, 1) == 'a') {
...
}
It's a large array, with almost a 1,000 objects in it. My goal is eventually append them to a dive so it looks something like this:
4
4 pints
4 biscuits
A
Apple
Alex
Adam
B
Bob
Billy
you can loop throught your collections like this:
var groupedCollection = {};
for(...){//loop throug collection
var firstLetter = data[i].charAt(0);
if(groupedCollection[firstLetter] == undefined){
groupedCollection[firstLetter] = [];
}
groupedCollection[firstLetter].push(data[i]);
}
//groupedCollection now contait data in the form of {a: [], b:[], etc...}
Bubble sort will do this job for you. Example:
// sample array
var myArr = [
{id:"00", name:"Billy"},
{id:"00", name:"Apple"},
{id:"00", name:"4 biscuits"},
{id:"00", name:"Adam"},
{id:"00", name:"Alex"},
{id:"00", name:"4 pints"},
{id:"00", name:"Bob"}
];
// standard bubble sort algorithm
function bubbleSortByName(arr) {
for (var x = 0; x < arr.length; x++) {
for(var y = 0; y < arr.length-1; y++) {
// compare arr[].name.toLowerCase() i.e. b > a
if(arr[y].name.toLowerCase() > arr[y+1].name.toLowerCase()) {
var tmp = arr[y+1];
arr[y+1] = arr[y];
arr[y] = tmp;
}
}
}
return arr;
}
// sort the array
var sortedArr = bubbleSortByName(myArr);
// print the results
for (var i=0; i<sortedArr.length; i++)
document.write(sortedArr[i].name+"<br/>");
Or the same idea with an insertion sort algorithm:
// standard insertion sort algorithm
function insertionSortByName(arr) {
for(var j = 1; j < arr.length; j++) {
var key = arr[j];
var i = j - 1;
while(i >= 0 && arr[i].name.toLowerCase() > key.name.toLowerCase()) {
arr[i+1] = arr[i];
i = i - 1;
}
arr[i+1] = key;
}
return arr;
}
ES7 syntax
const sortAndGroup = async () => {
const sortedData = data.sort();
const reducedData = sortedData.reduce((items, dataElement) => {
if (!items.find(item => item.header === dataElement.charAt(0))) {
items.push({ header: dataElement.charAt(0) });
}
items.push({ name: dataElement });
return items;
}, []);
return reducedData.map(item => item.header || item.name);
};
sortAndGroup().then(result => console.log(result));

Categories

Resources