I'm having some issues translating a depth list into a nested object.
For example, i have a list like so:
"depth": [ 0, 1, 2, 3, 3, 2, 3, 3, 3 ],
And i need to come up with a recursive function to produce an object like this:
"depth": [
{
"type": 0,
"children": [
{
"type": 1,
"children": [
{
"type": 2,
"children":[
{ "type": 3, "children": []},
{ "type": 3, "children": []},
]
},
{
"type:": 2,
"children":[
{ "type": 3, "children": []},
{ "type": 3, "children": []},
{ "type": 3, "children": []},
]
}
]
}
]
}
]
}
So the rule here, the lower numbers are parents, the higher numbers are siblings to the previous lower number.
So far what i have come up with is:
const depth = [0, 1, 2, 3, 3, 2, 3, 3, 3]
// Start looping through all the numbers in the depth
for (let i = 0; i < depth.length; i++) { //depth.length
// As i loop i want to only look at the array i have explored
// So that i can find the parent that is 1 lower in the array
let getParent = depth.slice(0, i).lastIndexOf(depth[i] - 1) // position of last lower array
// Here i check if the current depth item is bigger than the currently lower Item in the array
if (depth[i] > depth[getParent]) {
console.log(depth[i] + " Nesting into " + depth[getParent]) // Is child of that item
}
}
I think this successfully maps the child to the parent. But now I'm stuck on a way to produce my desired result.
If anyone has an suggestions I would appreciate it a lot.
Thanks
It's easier to create a tree when using an Map or a dictionary (object) to retrieve parents. After reducing the entire tree to a Map, retrieve the root, which holds the entire tree.
const createItem = (type) => ({ type, children: [] })
const fn = (arr, root) => arr.reduce((acc, d) => {
const parent = d - 1
const item = createItem(d)
if(d !== root && !acc.has(parent)) acc.set(parent, createItem(parent))
if(d !== root) acc.get(parent).children.push(item)
return acc.set(d, item);
}, new Map()).get(root);
const depth = [0, 1, 2, 3, 3, 2, 3, 3, 3 ]
const result = fn(depth, 0)
console.log(result)
As you get the data in pre-order sequence, you don't actually need a map:
function convert(data) {
let i = 0;
function getChildren(type) {
let children = [];
while (data[i] === type) {
i++;
children.push({
type,
children: getChildren(type + 1)
});
}
return children;
}
return getChildren(0);
}
let data = [0, 1, 2, 3, 3, 2, 3, 3, 3];
console.log(convert(data));
Related
I have an array "source"
source : [
{
"id": 1,
"secondId": 1
},
{
"id": 2,
"secondId": 1
},
{
"id": 3,
"secondId": 1
}
]
I want to rename the secondId when there are duplicate like this:
[
{
"id": 1,
"secondId": 1
},
{
"id": 2,
"secondId": 1_2
},
{
"id": 3,
"secondId": 1_3
}
]
I have this so far:
for (i = 0 ; i < source.length ; i++) {
for (j = 0 ; j < source.length ; j++){
if (source[i]["id"] != source[j]["id"] && source[i]["secondId"] === source[j]["secondId"]){
source[j]["secondId"] += "_" + (i+1);
}
console.log(source[j]["secondId"]);
}
}
and I'm getting:
[
{
"id": 1,
"secondId": 1
},
{
"id": 2,
"secondId": 1_2
},
{
"id": 3,
"secondId": 1_2_3
}
]
I tried to use some:
if(source[j]["secondId"].includes("_"+ (i+1))){
console.log(source[j]["secondId"].split("_"+ (i+1)).shift());
}
but I'm getting:
"secondId": 1
"secondId": 1
"secondId": 1_2
How can I do it? Any tips please?
A version using Array.reduce:
let source = [
{
"id": 1,
"secondId": 1
},
{
"id": 2,
"secondId": 1
},
{
"id": 3,
"secondId": 1
}];
let output = Object.values(
source.reduce((a, e, i) => {
let testId = e.secondId.toString();
if (a[testId]) {
testId = testId.split("_")[0] + "_" + (i + 1);
}
a[testId] = {...e, secondId: testId};
return a;
}, {})
);
console.log(output);
This may be a solution to achieve the (assumed) desired objective:
Code Snippet
const markDupes = arr => (
arr.reduce(
(fin, {id, secondId}) => ({
tm: {
[secondId]: (fin.tm[secondId] || 0) + 1
},
newArr: [
...fin.newArr,
{id, secondId: secondId.toString() + (
fin.tm[secondId] > 0 ? `_${fin.tm[secondId]+1}` : ''
)}
]
}),
{ tm: {}, newArr: [] }
)?.newArr
);
const source = [{
"id": 1,
"secondId": 1
},
{
"id": 2,
"secondId": 1
},
{
"id": 3,
"secondId": 1
}
];
console.log(markDupes(source));
Explanation
Use .reduce to iterate over the array of objects
Set up an initial fin object with two props tm (tracking-map) and newArr (the result-array which will have the secondId updated as desired)
For each object, destructure to directly access id and secondId
Update the map tm based on the secondId with a counter
Append to the newArr an object with id and secondId props with the latter (secondId) being converted to a string to store values of the format 1_2, 1_3, etc
NOTE: This may not be an optimal solution.
When you convert 1 to 1_2 or 1_3 it is converting a number to a string which will be a huge pain when you have a use for the number later. Instead what i have done is convert that number to a decimal for as 1.2 ,1.3 which means you can do all sorts of computation on a number without much conversion
let source = [
{
"id": 1,
"secondId": 1
},
{
"id": 2,
"secondId": 1
},
{
"id": 3,
"secondId": 1
}
];
let val = {};
for (const i in source) {
let v = source[i].secondId
val[v] = val[v] ? val[v] : v
if (val[v] !== 1) {
console.log(source[i].id, v);
source[i].secondId = Number(v.toFixed(2)) + (val[v] * 0.1)
}
val[v]++
}
console.log(source);
if you are really kean of using string instead use source[i].secondId = v+'_'+val[v] instead inside the if by changing the original source json object as well
I need a function to filter array of objects based on given structure of object. So I have this object:
{
"2": [
{
"fd_id": 16,
...others
}
],
"3": [
{
"fd_id": 2,
...others
},
{
"fd_id": 3,
...others
}
]
}
I would like to filter another array based on this object. Like this;
const result = products.filter(item => {
// returns array of numbers [1, 2, 3]
const filters = item.filters;
if(filters){
// Here must be refactored
return ((filters.includes(givenObj[2][0].fd_id))
&& (filters.includes(givenObj[3][0].fd_id) || filters.includes(givenObj[3][1].fd_id)));
}
});
But this function must be dynamic. Because the input object may change. So for between each parent "&&", and between each children "||" condition must be applied. Thanks for any help. This is the link to example https://jsfiddle.net/cadkt86n/
A function to loop the data will help.
My Logic
Generate the list of fd_ids from the groups using Array.map
Filter products array. Check for the matching combination in filters node of products array. Condition is there should be a matching combination in each nodes of fdIdList array.
Working Fiddle
var groups = {
"2": [
{ "fd_id": 16, "fd_fRef": 2, "fd_ad": "35 - 50", "fd_siraNo": 255, "checked": true }
],
"3": [
{ "fd_id": 2, "fd_fRef": 3, "fd_ad": "KURU", "fd_siraNo": 255, "checked": true },
{ "fd_id": 3, "fd_fRef": 3, "fd_ad": "KARMA", "fd_siraNo": 255, "checked": true }
]
}
// Aggregates the list of fd_id s - This wil be an array of arrays
// [[16],[2,3]] => This will be the value
const fdIdList = Object.values(groups).map(a => a.map(b => b.fd_id));
var products = [
{
"id": 1,
"filters": [2, 3, 4, 13, 16, 17, 18, 19, 31, 48, 309, 318],
},
{
"id": 2,
"filters": [2, 3, 4, 13, 15, 17, 18, 19, 31, 48, 309, 318],
}
];
// Check if there is a common element in each node of fdIdList
var result = products.filter(item => {
const filters = item.filters;
if (filters) {
let isFound = true;
fdIdList.forEach(idListNode => {
isFound = isFound && idListNode.filter(value => filters.includes(value)).length > 0;
})
return isFound
}
});
console.log(result)
I'm currently working with an array of nested arrays and attempting to pull out certain arrays if they meet a condition (length greater than 1). Here's the array -
const testArr = [
{
"type": 1,
"categories": [
{
"description": "Foo",
"subOptions": [
{
"subType": 1,
"subTypeTypeDescription": "Bar"
}
],
}
]
},
{
"type": 2,
"categories": [
{
"description": "Baz",
"subOptions": [
{
"subType": 2,
"subTypeTypeDescription": "Baf"
},
{
"subType": 3,
"subTypeTypeDescription": "Bee"
}
],
}
]
}
]
What I'm hoping to produce is an array of categories, but only if those categories have a subOptions array with a length > 1. Here is the hoped for result on the array above -
[
{
"description": "Baz",
"subOptions": [
{
"subType": 2,
"subTypeTypeDescription": "Baf"
},
{
"subType": 3,
"subTypeTypeDescription": "Bee"
}
],
}
]
I can get this to work by using normal foreach loops like so -
let categories: [];
testArr.forEach((service) => {
service.categories.forEach((cat) => {
if(cat.subOptions.length > 1) {
categories.push(cat)
}
})
})
but I'm trying to learn how to do these things with functional tools. I've tried to do it now several times with a mix of map, filter, and reduce and keep failing. If anyone can offer some direction on the most optimal way to do the same thing with functional methods, I'd greatly appreciate it.
Use a .filter that checks the length of the sub-array:
const testArr=[{type:1,categories:[{description:"Foo",subOptions:[{subType:1,subTypeTypeDescription:"Bar"}]}]},{type:2,categories:[{description:"Baz",subOptions:[{subType:2,subTypeTypeDescription:"Baf"},{subType:3,subTypeTypeDescription:"Bee"}]}]}];
const result = testArr
.flatMap(item => item.categories)
.filter(({ subOptions }) => subOptions.length >= 2);
console.log(result);
Try this
const categories = testArr.map(service => {
return service.categories.filter(category => category.subOptions.length > 1);
})
Or even this in one line:
const categories = testArr.map(service => service.categories.filter(category => category.subOptions.length > 1));
I've been trying to make a function that generates all the permutations of the numbers from 0 to num and stores them in a multidimensional array. I want to store in the combinations variable something like:
[ [ 1, 2, 3 ],
[ 1, 3, 2 ],
[ 2, 1, 3 ],
[ 3, 1, 2 ],
wrongly [ 2, 3, 1 ],
[ 3, 2, 1 ] ]
but instead I get:
[ [ 3, 2, 1 ],
[ 3, 2, 1 ],
[ 3, 2, 1 ],
[ 3, 2, 1 ],
[ 3, 2, 1 ],
[ 3, 2, 1 ] ]
My function is:
var combinations = [];
function comb(num, index, list, used) {
if (num == index)
combinations.push(list);
else {
for (var i = 0; i < num; ++i) {
if (!used[i]) {
list[i] = index + 1;
used[i] = true;
comb(num, index + 1, list, used);
used[i] = false;
}
}
}
}
I usually program with C++, so I think that I am using the arrays wrongly.
Your issue is that the list array you pass into your recursive call is going to be changed as it is essentially passed by reference, so list will lose its previous combinations. Instead, you can make a copy of the array before you pass it into your recursive call using the spread syntax (...).
See working example below:
var combinations = [];
function comb(num, index, list, used) {
if (num == index)
combinations.push(list);
else {
for (var i = 0; i < num; ++i) {
if (!used[i]) {
list[i] = index + 1;
used[i] = true;
comb(num, index + 1, [...list], used);
used[i] = false;
}
}
}
}
comb(3, 0, [], []);
console.log(combinations); // [[1,2,3],[1,3,2],[2,1,3],[3,1,2],[2,3,1],[3,2,1]]
.as-console-wrapper { max-height: 100% !important; top: 0;}
I want to iterate over a JSON in javasacript and create something like this as output
{
"seriesData": [
{
"name": "John",
"data": [
5,
3,
4
]
},
{
"name": "Jane",
"data": [
2,
2,
3
]
},
{
"name": "Joe",
"data": [
3,
4,
4
]
}
]
}
So, I essentially need to add values in data array for each key inside my for loop.
Input looks like:
{"Records":[{"name":"Jan'10","users":[{"name":"John","y":5},{"name":"Jane","y":2},{"name":"Joe","y":3}]},{"name":"Jan'10","users":[{"name":"John","y":3},{"name":"Jane","y":2},{"name":"Joe","y":4}]},{"name":"Jan'10","users":[{"name":"John","y":4},{"name":"Jane","y":3},{"name":"Joe","y":4}]}]};
Can someone please suggest how can this be achieved.
you could try something like this:
var dataList = {};
function addData(name){
if( dataList[name] == undefined)
dataList[name] = [];
for (var i = 1; i < arguments.length; i++) {
dataList[name].push(arguments[i]);
}
}
function packData(){
var pack = []
for(var e in dataList){
pack.push({
name: e,
data:dataList[e].sort(function(a,b){return a-b})
});
}
return pack;
}
addData("Joe", 1);
addData("Jan", 2, 10);
addData("Joe", 3, 5, 10, 18, 500);
addData("Jan", 4);
addData("Joe", 5);
addData("Jan", 6);
console.log( packData() );
use addData(name, data); to append data to a name and afterwards pack this data with packData()
EDIT:
Sry switched to PHP for a while... fixed the script XD