Build nested object arrays within recursive function - javascript

As per my original question geared specifically for React (React recursive tree pass JSON path), I have realised the problem is pretty generic.
I have a recursive function that essentially loops through a treelike JSON structure, each time it outputs a branch I want to pass an object of the structures location in the tree like below.. Is there a simpler way to pass the structure? Is the data structure poor / should each chid have a unique ID attached?
My JSON object is below so you can see what I'm working with.
Any help much appreciated!
Level 1 child
{value: "Fruit"}
Level 2 child
{value: "Fruit", nested_values: [{ value: 'Tropical'}] }
Level 3 child
{value: "Fruit", nested_values: [{ value: 'Tropical', nested_values:[{ value: 'Pineapple' }]}] }
Code - kind of works, but then I get all values within the same nested_values array
const createSelectionHierarchy = (data, isSub, level = 2, hierarchy = {}) => {
let children = [];
if (isSub) { level++; }
let obj = {};
obj.name = cat;
const cat = hierarchy.value;
for (let i in data) {
const subcat = data[i].value;
if (typeof(data[i].nested_values) === 'object') { // Sub array found, build structure
obj.values = subcat;
obj.nested_values = [];
hierarchy.nested_values.push(obj);
children.push(
<FilterItem key={i} data={data[i]} hierarchy={hierarchy} level={level}>
{createSelectionHierarchy(data[i].nested_values, true, level, hierarchy)}
</FilterItem>
);
} else { // No sub array, bottom of current branch
children.push(
<p className="filter-item level-last" key={i}>
{data[i].value}
</p>);
}
}
return children;
}
JSON
{
"value": "Fruit",
"occurrence_count": 5,
"nested_values": [{
"value": "Berries",
"occurrence_count": 3,
"nested_values": [{
"value": "Strawberry",
"occurrence_count": 1
}, {
"value": "Blackberry",
"occurrence_count": 1
}, {
"value": "Raspberry",
"occurrence_count": 1
}, {
"value": "Redcurrant",
"occurrence_count": 1
}, {
"value": "Blackcurrant",
"occurrence_count": 1
}, {
"value": "Gooseberry",
"occurrence_count": 1
}, {
"value": "Cranberry",
"occurrence_count": 1
}, {
"value": "Whitecurrant",
"occurrence_count": 1
}, {
"value": "Loganberry",
"occurrence_count": 1
}, {
"value": "Strawberry",
"occurrence_count": 1
}]
}, {
"value": "Tropical",
"occurrence_count": 2,
"nested_values": [{
"value": "Pineapple",
"occurrence_count": 1
}, {
"value": "Mango",
"occurrence_count": 1
}, {
"value": "Guava",
"occurrence_count": 1
}, {
"value": "Passion Fruit",
"occurrence_count": 1
}, {
"value": "Dragon Fruit",
"occurrence_count": 1
}]
}]
}
Desired output
<FilterItem ...... hierarchy={{value: "Fruit"}}>
<FilterItem ...... hierarchy={{value: "Fruit", nested_values: [{ value: 'Tropical'}] }}>
<FilterItem ...... hierarchy={{value: "Fruit", nested_values: [{ value: 'Tropical', nested_values:[{ value: 'Pineapple' }]}] }}>
</FilterItem>
</FilterItem>
</FilterItem>

For each branch (level of tree), this function will extract the value and then call itself on each of its children, if there are any, and store their return values in an array.
function convert(branch) {
const hierarchy = {
value: branch.value
};
if (branch.nested_values !== undefined)
hierarchy.nested_values = branch.nested_values.map(subranch => convert(subranch))
return hierarchy;
}
const input = {
"value": "Fruit",
"occurrence_count": 5,
"nested_values": [{
"value": "Berries",
"occurrence_count": 3,
"nested_values": [{
"value": "Strawberry",
"occurrence_count": 1
}, {
"value": "Blackberry",
"occurrence_count": 1
}, {
"value": "Raspberry",
"occurrence_count": 1
}, {
"value": "Redcurrant",
"occurrence_count": 1
}, {
"value": "Blackcurrant",
"occurrence_count": 1
}, {
"value": "Gooseberry",
"occurrence_count": 1
}, {
"value": "Cranberry",
"occurrence_count": 1
}, {
"value": "Whitecurrant",
"occurrence_count": 1
}, {
"value": "Loganberry",
"occurrence_count": 1
}, {
"value": "Strawberry",
"occurrence_count": 1
}]
}, {
"value": "Tropical",
"occurrence_count": 2,
"nested_values": [{
"value": "Pineapple",
"occurrence_count": 1
}, {
"value": "Mango",
"occurrence_count": 1
}, {
"value": "Guava",
"occurrence_count": 1
}, {
"value": "Passion Fruit",
"occurrence_count": 1
}, {
"value": "Dragon Fruit",
"occurrence_count": 1
}]
}]
};
function convert(branch) {
const hierarchy = {
value: branch.value
};
if (branch.nested_values !== undefined)
hierarchy.nested_values = branch.nested_values.map(subranch => convert(subranch))
return hierarchy;
}
console.log(convert(input));
On a side note, what you supplied is not valid JSON (keys mustn't have quotes), it is a JavaScript object. To get an object from a JSON string, you have to call JSON.parse().

Related

Compare value to get largest number in every object looping JavaScript

I have a problem with how to compare value to get largest number in more than one object.
For example i have 3 object (and this object can increment more than 3):
These each object saved in acc variable
[
{ "key": 1, "value": 1 },
{ "key": 1, "value": 3 },
{ "key": 1, "value": 6 },
]
[
{ "key": 2, "value": 2 },
{ "key": 2, "value": 5 },
{ "key": 2, "value": 9 },
]
[
{ "key": 3, "value": 1 },
{ "key": 3, "value": 2 },
{ "key": 3, "value": 3 },
]
First i get the last value from each object with console.log(acc[acc.length - 1].value);
and it will print:
6
9
2
Then i don't know how to compare the numbers? And get result:
{ "key": 2, "value": 9 }
I have try console.log(Math.max(acc[acc.length - 1].value));, but its not working because that number is not inside one array.
This is screenshot if i log console.log(acc)
First merge all the nested objects by .flat() and perform .reduce() operation on them. in every iteration look b.value is greater than the object value of accumulator a.value, if it's the case we need to update the object of the accumulator a.
const arr = [[ { "key": 1, "value": 1 }, { "key": 1, "value": 3 }, { "key": 1, "value": 6 }, ], [ { "key": 2, "value": 2 }, { "key": 2, "value": 5 }, { "key": 2, "value": 9 }, ], [ { "key": 3, "value": 1 }, { "key": 3, "value": 2 }, { "key": 3, "value": 3 }, ]];
console.log(arr.flat().reduce((a,b)=>(b.value > a.value ? b : a)));
New requirement:
const arr = [[
{ "key": 1, "value": 1 },
{ "key": 1, "value": 3 },
{ "key": 1, "value": 6 },
{ "key": 3, "value": 6 },
],
[
{ "key": 2, "value": 2 },
{ "key": 2, "value": 5 },
{ "key": 2, "value": 9 },
],
[
{ "key": 3, "value": 1 },
{ "key": 3, "value": 2 },
{ "key": 3, "value": 3 },
]];
let results = [{key: -1, value: -1}]; //initial value I assume data don't have a value less than -1
arr.forEach((item) => { //Simulating your current situation
item.forEach(it => { //here we get an array in each iteration
if (it.value > results[0].value) {
results = [it];
}
else if (it.value === results[0].value) {
results.push(it);
}
});
});
console.log(results);

How to split array in array to json ? Map Javascript

All code is here:
https://stackblitz.com/edit/angular-foormat-wi?file=src/app/app.component.ts
You can see array with 3 object but inside object also you can see array...
I need split all array to be object.
Check code:
this.allFilters.push(
Array.isArray(data.value)
? data.value.map((obj: any) => {
return {
name: obj.name,
value: obj.value
};
})
: { name: data.name, value: data.value }
);
Html print not important this is only for view json.
Right now my allFilters var is like :
[{ "name": "one", "value": 1 }, { "name": "two", "value": 2 }, { "name": "three", "value": 3 }],
{"name": "Second", "value": 15 },
[{ "name": "one", "value": 1 }, { "name": "two", "value": 2 }, { "name": "three", "value": 3 }, { "name": "four", "value": 4 }]
Array with inside Array, Object, Array but I need like this:
[
{ "name": "one", "value": 1 }, { "name": "two", "value": 2 }, { "name": "three", "value": 3 },
{"name": "Second", "value": 15 },
{ "name": "one", "value": 1 }, { "name": "two", "value": 2 }, { "name": "three", "value": 3 }, { "name": "four", "value": 4 }
]
I expect array of objects.
No array of mix array and object.
Just object inside.
When data.value is an array and even if you do map it will give you the same result.
Modify your recieveMessage(data) and use array.concat() when it is an array.
recieveMessage(data) {
if (Array.isArray(data.value)) {
this.allFilters = this.allFilters.concat(data.value);
} else {
this.allFilters.push({ name: data.name, value: data.value });
}
}
With ternary operator,
recieveMessage(data) {
Array.isArray(data.value) ?
this.allFilters = this.allFilters.concat(data.value)
:
this.allFilters.push({ name: data.name, value: data.value });
}
Just spread the map() result
const allFilters = []
const recieveMessage = (data) => {
if (Array.isArray(data.value)) {
allFilters.push(...data.value.map(obj => ({ ...obj})))
} else {
allFilters.push({ ...data})
}
}
data.forEach(recieveMessage)
console.log(allFilters)
<script>
const data=[{name:"First",value:[{name:"one",value:1},{name:"two",value:2},{name:"three",value:3}]},{name:"Second",value:15},{name:"Third",value:[{name:"one",value:1},{name:"two",value:2},{name:"three",value:3},{name:"four",value:4}]}];
</script>

filter nested array with Javascript

having an array with objects and inside an options array how do I filter the inner array of objects by key value?
Here is the following example:
let test = [{
"options": [{
"label": "Audi",
"value": 10
},
{
"label": "BMW",
"value": 18
},
{
"label": "Mercedes Benz",
"value": 116
},
{
"label": "VW",
"value": 184
}
],
"label": "test1"
},
{
"options": [{
"label": "Adler",
"value": 3664
},
{
"label": "Alfa Romeo",
"value": 3
},
{
"label": "Alpine",
"value": 4
}
],
"label": "test2"
}
]
how do I get back the object:
{
"label": "Audi",
"value": 10
}
if I filter with keyword Audi
return label.toLowerCase().includes(inputValue.toLowerCase());
I tried with the following
test.map((k) => {
res = k.options.filter((j) => {
inputValue.toLowerCase();
if (j.label.toLowerCase().includes(inputValue.toLowerCase())) {
return j;
}
});
});
You need to return the result of filter(), not just assign it to a variable, so that map() will return the results.
let test = [{
"options": [{
"label": "Audi",
"value": 10
},
{
"label": "BMW",
"value": 18
},
{
"label": "Mercedes Benz",
"value": 116
},
{
"label": "VW",
"value": 184
}
],
"label": "test1"
},
{
"options": [{
"label": "Adler",
"value": 3664
},
{
"label": "Alfa Romeo",
"value": 3
},
{
"label": "Alpine",
"value": 4
}
],
"label": "test2"
}
]
let inputValue = "audi";
let search = inputValue.toLowerCase();
let result = test.map(k => k.options.filter(j => j.label.toLowerCase().includes(search)));
console.log(result);
This will return all options matching the search query :
function find(array, query) {
return array.reduce((prev, current) => prev.concat(current.options), []).filter(item => item.label.includes(query))
}
find(test, 'Audi')
Use flatMap() followed by filter():
let test=[{options:[{label:"Audi",value:10},{label:"BMW",value:18},{label:"Mercedes Benz",value:116},{label:"VW",value:184}],label:"test1"},{options:[{label:"Adler",value:3664},{label:"Alfa Romeo",value:3},{label:"Alpine",value:4}],label:"test2"}]
let result = test.flatMap(el => {
return el.options.filter(car => car.label == "Audi")
})[0]
console.log(result)
You need to go two levels deep when traversing your array.
function filterArray(needle, haystack) {
return haystack
.map(h => h.options)
.flatMap(h => h )
.filter(h => h.label.toLowerCase().includes(needle.toLowerCase()));
CodeSandbox
This might gile your answer:
console.log(test[0].options[0]);

Dynamically create array of objects from the object with the array values

Currently I have the below object structure,
`let selectedOptions = {
"color": {
"id": "2",
"values": [
{
"value": "red",
"label": "Red",
"id": 1
},
{
"value": "blue",
"label": "Blue",
"id": 2
}
]
},
"size": {
"id": "19",
"values": [
{
"value": "medium",
"label": "Medium",
"id": 2
}
]
},
"demo": {
"id": "19",
"values": [
{
"value": "neylon",
"label": "Neylon",
"id": 2
}
]
}
.
.
.
N
}; `
And want to create array of objects from the above object like as below,
[
{ color: "red", size: "medium", demo: "neylon" },
{ color: "blue", size: "medium", demo: "neylon" }
]
I have tried like below but it didn't worked
https://jsfiddle.net/6Lvb12e5/18/
let cArr = [];
for(key in selectedOptions) {
selectedOptions[key].values.forEach(function(val,i) {
cArr.push({ [key]: val.value })
})
}
Thanks
You could take the wanted parts, like color, size and demo and build a cartesian product out of the given data.
const
cartesian = (a, b) => a.reduce((r, v) => r.concat(b.map(w => [].concat(v, w))), []),
options = { color: { id: "2", values: [{ value: "red", label: "Red", id: 1 }, { value: "blue", label: "Blue", id: 2 }] }, size: { id: "19", values: [{ value: "small", label: "Small", id: 1 }, { value: "medium", label: "Medium", id: 2 }] }, demo: { id: "19", values: [{ value: "neylon", label: "Neylon", id: 2 }] } },
parts = Object
.entries(options)
.map(([k, { values }]) => [k, values.map(({ value }) => value)]),
keys = parts.map(([key]) => key),
result = parts
.map(([, values]) => values)
.reduce(cartesian)
.map(a => Object.assign(...a.map((v, i) => ({ [keys[i]]: v }))));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I am not sure that is this the best way to do this, and even the data is missing but as a comment, it is quite big to post but can try:
let selectedOptions = {
"color": {
"id": "2",
"values": [
{
"value": "red",
"label": "Red",
"id": 1
},
{
"value": "blue",
"label": "Blue",
"id": 2
}
]
},
"size": {
"id": "19",
"values": [
{
"value": "medium",
"label": "Medium",
"id": 2
}
]
},
"demo": {
"id": "19",
"values": [
{
"value": "neylon",
"label": "Neylon",
"id": 2
}
]
}
};
let cArr = [];
var obj = {};
var glob;
for(key in selectedOptions) {
selectedOptions[key].values.forEach(function(val,i) {
obj[key] = val.value;
}
)
glob = obj;
}
cArr.push(glob);
console.log(cArr)

Maximum value from the JSON

I needed the maximum value from the particular JSON object. I needed it to make a scale for apple and banana's for d3 from its maximum value
So how do I get the maximum value of apple and banana from this JSON. I don't need the month name. I don't want to use any for or while loops.
var arr = [
{
"date":"Jan",
"values": [
{"name":"apple","value":100},
{"name":"banana","value":200}
]
},
{
"date":"Feb",
"values": [
{"name":"apple","value":300},
{"name":"banana","value":455}
]
},
{
"date":"Mar",
"values": [
{"name":"apple","value":588},
{"name":"banana","value":700}
]
}
];
You can also use Math.max after finding the fruit from values with find:
var arr = [{
"date": "Jan",
"values": [{
"name": "apple",
"value": 100
},
{
"name": "banana",
"value": 200
}
]
},
{
"date": "Feb",
"values": [{
"name": "apple",
"value": 300
},
{
"name": "banana",
"value": 455
}
]
},
{
"date": "Mar",
"values": [{
"name": "apple",
"value": 588
},
{
"name": "banana",
"value": 700
}
]
}
];
const maxVal = (fruit) => Math.max(...arr.map(d => d.values.find(v => v.name === fruit).value));
console.log(maxVal('apple'));
console.log(maxVal('banana'));
You could something like this:
const max = (list) => list.reduce((acc, cur) => {
let apple = cur.values[0].name === 'apple' ? cur.values[0] : cur.values[1];
let banana = cur.values[0].name === 'banana' ? cur.values[0] : cur.values[1];
acc[0] = acc[0] < apple.value ? apple.value : acc[0];
acc[1] = acc[1] < banana.value ? banana.value : acc[1];
return acc;
}, [0, 0]);
It returns an array, the first item is the max number of apples, the second is the max number of bananas.
Example
var arr = [{
"date": "Jan",
"values": [{
"name": "apple",
"value": 100
},
{
"name": "banana",
"value": 200
}
]
},
{
"date": "Feb",
"values": [{
"name": "apple",
"value": 300
},
{
"name": "banana",
"value": 455
}
]
},
{
"date": "Mar",
"values": [{
"name": "apple",
"value": 588
},
{
"name": "banana",
"value": 700
}
]
}
];
const max = (list) => list.reduce((acc, cur) => {
let apple = cur.values[0].name === 'apple' ? cur.values[0] : cur.values[1];
let banana = cur.values[0].name === 'banana' ? cur.values[0] : cur.values[1];
acc[0] = acc[0] < apple.value ? apple.value : acc[0];
acc[1] = acc[1] < banana.value ? banana.value : acc[1];
return acc;
}, [0, 0]);
console.log(
max(arr)
);

Categories

Resources