array to nested array conversion - javascript

my backend service send me list of node as an array. but I need is, each next node is value of its previous node(SEE EXAMPLE). I want whole list as nested object in singe object.
WHAT I HAVE:
[
{
"nodeId": 1,
},
{
"nodeId": 3,
},
{
"nodeId": 16,
}
]
WHAT I NEED:
[
{
"nodeId": 1,
"staticChild": [
{
"nodeId": 3,
"staticChild": [
{
"nodeId": 16,
}
]
}
]
}
]

You could reduce the array from the right side and build a new object with a staticChild property.
var array = [{ nodeId: 1 }, { nodeId: 3 }, { nodeId: 16 }],
result = array.reduceRight((a, b) => ({ ...b, staticChild: [a] }));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Based on the input / output you provided, you can use a recursive funtion like :
const data = [{
nodeId: 1
},
{
nodeId: 3
},
{
nodeId: 16
}
];
const transform = data => {
const [node, ...rest] = data;
if (rest.length > 0) {
return {
...node,
staticChild: [transform(rest)]
};
} else {
return {
...node,
hasChildren: false
};
}
};
const result = transform(data);
console.log(result);

At first reverse the array and the make an iteration over the revered array using reduce() to make your desire format.
let data = [{"nodeId": 1},{"nodeId": 3},{"nodeId": 16}]
data = data.reverse().reduce((old, cur) => {
if (!old.length) {
old = [cur]
} else {
cur['staticChild'] = old
old = [cur]
}
return old
}, [])
console.log(data)

You can use the reduceRight() array method to perform the transformation.
const data = [{
"nodeId": 1,
},
{
"nodeId": 3,
},
{
"nodeId": 16,
}
]
const nested = data.reduceRight((acc, item) => {
return [ { ...item, staticChild: acc } ]
}, []);
console.log(nested);
Or more succinctly:
const nested = data.reduceRight((acc, item) => [ { ...item, staticChild: acc } ],[]);

Related

How to flatten nested array of objects and create new array

I have a data structure as the following
[
{
"models":[
{
"name":"xyz",
"options":[
{
"label":"blue"
},
{
"label":"brown"
},
]
},
{
"name":"abc",
"options":[
{
"label":"yellow"
}
]
},
{
"name":"def",
"options":[
{
"label":"green"
}
]
}
]
}
]
The end result should be an array with all of the labels and name like xyz: blue, xyz: brown, abc: yellow, def: green
so something like this
['xyz: blue', 'xyz: brown', 'abc: yellow','def: green']
I'm trying different approaches, one with RxJS operators and another with reduce
let flat = (acc, current) => {
}
models.reduce(flat, [])
You can use a reduce and a map like this.
const arr = [
{
"models":[
{
"name":"xyz",
"options":[
{
"label":"blue"
},
{
"label":"brown"
},
]
},
{
"name":"abc",
"options":[
{
"label":"yellow"
}
]
},
{
"name":"def",
"options":[
{
"label":"green"
}
]
}
]
}
];
const result = arr[0].models.reduce(
(acc, model) => [...acc, ...model.options.map(i => ({ [model.name]: i.label }))]
, []
);
console.log(result);
If the top level array can have multiple items rather than arr[0] you would need to add another reduce feeding it's accumulator in to the second reduce as it's starting accumulator rather than the empty starting array.
const arr = [
{
"models":[
{
"name":"xyz",
"options":[
{
"label":"blue"
},
{
"label":"brown"
},
]
},
{
"name":"abc",
"options":[
{
"label":"yellow"
}
]
},
{
"name":"def",
"options":[
{
"label":"green"
}
]
}
]
},
{
"models":[
{
"name":"ghi",
"options":[
{
"label":"gold"
},
{
"label":"pink"
},
]
}
]
}
];
const result = arr.reduce(
(acc, item) =>
item.models.reduce(
(acc2, model) => [...acc2, ...model.options.map((i) => ({ [model.name]: i.label }))]
, acc
),
[]
);
console.log(result);
Not sure where RxJs comes into this question but if you are looking to transform an object like this that comes back from a http request you would pipe it into the map operator and then use this function inside the map. If you are looking to do a reduce on a stream there is a reduce operator that emits the accumulator when the source stream completes or the scan operator that emits the accumulator each time the source emits.
Use nested calls to flatMap(), and in the innermost call you concatenate the model name with the option label.
const data = [{
"models": [{
"name": "xyz",
"options": [{
"label": "blue"
},
{
"label": "brown"
},
]
},
{
"name": "abc",
"options": [{
"label": "yellow"
}]
},
{
"name": "def",
"options": [{
"label": "green"
}]
}
]
}];
let result = data.flatMap(d => d.models.flatMap(model => model.options.map(option => `${model.name}: ${option.label}`)));
console.log(result);
Here is using multiple forEach and destructuring
const flat = (arr, res = []) => {
arr.forEach(({ models }) =>
models.forEach(({ name, options }) =>
options.forEach(({ label }) => res.push({ [name]: label }))
)
);
return res;
};
const data = [
{
models: [
{
name: "xyz",
options: [
{
label: "blue",
},
{
label: "brown",
},
],
},
{
name: "abc",
options: [
{
label: "yellow",
},
],
},
{
name: "def",
options: [
{
label: "green",
},
],
},
],
},
];
console.log(flat(data));
const response = array[0].models.reduce((initial, model) => {
if (model.options.length === 1)
initial.push(`${model.name}: ${model.options[0].label}`);
else {
model.options.forEach((option) =>
initial.push(`${model.name}: ${option.label}`),
);
}
return initial;
}, []);
console.log(response)
;
// there is no need if the inner option has just one object you can just access it by model.options[0].label, that why there is a check to see if it one

function to generate n length m depth data

I am trying to generate tree structure data, the problem is in helper function where I have an empty array children in which I want to push object with data, but get undefined
I do not get undefined when test it separately.
let b = {x: 1}
let a = []
a.push(b)
console.log(a)
function forestMockDataGenerator(n, m) {
const chance = new Chance(Math.random());
const mock = Array.from(Array(n).keys());
return mock.map(function (tree) {
tree = {
datum: chance.name(),
children: [],
};
return helper(tree, 1);
function helper(mock, count) {
if (isCountNotEqualToM(count, m)) {
mock.children.push({
datum: equalOneAddressEqualTwoPhone(count),
children: [],
});
helper(mock.children, (count = count + 1));
}
function equalOneAddressEqualTwoPhone(count) {
return count === 1 ? chance.address() : chance.phone();
}
function isCountNotEqualToM(count, m) {
return count !== m ? true : false;
}
return mock;
}
});
}
example 1
example 2
the data should have the next format
[
{
"name": "String",
"children": [
{
"name": "String",
"children": [
// ...
]
}, {
"name": "String",
"children": [
// ...
]
},
// ...
]
}, {
"name": "String",
"children": [
// ...
]
},
// ...
]
You yould use independent creation of children by handing over the decremented depth.
function forestMockDataGenerator(n, m) {
if (!m) return [];
return Array.from({ length: n }, (_, i) => ({
name: `name${i}`,
children: forestMockDataGenerator(n, m - 1),
}));
}
console.log(forestMockDataGenerator(5, 3));
.as-console-wrapper { max-height: 100% !important; top: 0; }

Filter selected children from data using Javascript

I try to write a function in JavaScript which filter an array by a selected property (an value).
But it works for 2 level only I do not understand what do I missing.
The data I want to filter:
var data = [
{
name: "john_pc",
children: [
{
name: "sabrina_pc",
children: [
{
name: "sabrina_pc"
},
{
name: "john_pc"
}
]
},
{
name: "john_pc"
}
]
},
{
name: "sabrina_pc"
}
]
The childrenFilter funciton :
const childrenFilter = (childrenData, filters) => {
let filteredData = childrenData.filter(item => {
for (var property in filters) {
var optionalValues = filters[property];
var value = item[property];
if (item.children) {
item.children = childrenFilter(item.children, filters);
}
let hasValue = value == optionalValues;
if (hasValue) {
return true;
}
return false;
}
return false;
}, this);
return filteredData;
}
Calling the function:
As you can see the 'childrenFilter' get an object which the key is property in the data and the key is value I want to keep.
let result = childrenFilter(data, {
"name": "a1"
});
console.log(JSON.stringify(result, null, 2))
The wanted result :
[
{
"name": "john_pc",
"children": [
{
"name": "sabrina_pc",
"children": [
{
"name": "john_pc"
}
]
},
{
"name": "john_pc"
}
]
}
]
Your filter function does not take into account whether or not children elements match the pattern, therefore even though some child elements of the object match the pattern, the object itself is being filtered out.
Here is the explanation:
{
name: "a2", // does not match filter {name:'a1} so is removed alongside child objects
children: [ // gets removed with parent object
{
name: "a2"
},
{
name: "a1"
}
]
}
This should produce the desired output:
const childrenFilter = (childrenData, filters) => {
let filteredData = childrenData.filter(item => {
for (var property in filters) {
var optionalValues = filters[property];
var value = item[property];
if (item.children) {
item.children = childrenFilter(item.children, filters);
}
let hasValue = value == optionalValues;
if (hasValue || item.children.length) { // include item when children mathes the pattern
return true;
}
return false;
}
return false;
}, this);
return filteredData;
}
You could build new array for each step of filtering, beginning from the leaves and check if this contains the wanted value.
This approach generates new objects and does not mutate the original data.
function filter(array, filters) {
return array.reduce((r, o) => {
var children = filter(o.children || [], filters);
return children || Object.entries(filters).every(([k, v]) => o[k] === v)
? (r || []).concat(Object.assign({}, o, children && { children }))
: r;
}, undefined);
}
var data = [{ name: "a1", children: [{ name: "a2", children: [{ name: "a2" }, { name: "a1" }] }, { name: "a1" }] }, { name: "b1" }];
console.log(filter(data, { name: "a1" }));
.as-console-wrapper { max-height: 100% !important; top: 0; }

Distinct Dates with its highest value from an object with javascript

I have something like this:
data = [
{
DateMeasured:"2018-08-27T04:46:25",
Steps:100
},
{
DateMeasured:"2018-08-27T04:46:25",
Steps:500
},
{
DateMeasured:"2018-08-27T04:46:25",
Steps:800
},
{
DateMeasured:"2018-08-26T04:46:25",
Steps:400
},
{
DateMeasured:"2018-08-26T04:46:25",
Steps:300
},
{
DateMeasured:"2018-08-25T04:46:25",
Steps:100
}
];
I have an object of data like above, now I want to recreate object with discrict dates but its highest steps, but now i want like this:
data = [
{
DateMeasured:"2018-08-27T04:46:25",
Steps:800
},
{
DateMeasured:"2018-08-26T04:46:25",
Steps:400
},
{
DateMeasured:"2018-08-25T04:46:25",
Steps:100
}
];
How can I achieve this goal?
You could reduce the array by checking the last inserted object with the same date and if not found, insert the object, otherwise check the value and update the array with a greater Step property.
var data = [{ DateMeasured: "2018-08-27T04:46:25", Steps: 100 }, { DateMeasured: "2018-08-27T04:46:25", Steps: 500 }, { DateMeasured: "2018-08-27T04:46:25", Steps: 800 }, { DateMeasured: "2018-08-26T04:46:25", Steps: 400 }, { DateMeasured: "2018-08-26T04:46:25", Steps: 300 }, { DateMeasured: "2018-08-25T04:46:25", Steps: 100 }],
result = data.reduce((r, o) => {
var index = r.findIndex(({ DateMeasured }) => DateMeasured === o.DateMeasured);
if (index === -1) {
r.push(o);
return r;
}
if (r[index].Steps < o.Steps) {
r[index] = o;
}
return r;
}, []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
If you need to sort by the number of steps and take the first 3 elements:
const data = [ { DateMeasured:"2018-08-27T04:46:25", Steps:100 }, { DateMeasured:"2018-08-27T04:46:25", Steps:500 }, { DateMeasured:"2018-08-27T04:46:25", Steps:800 }, { DateMeasured:"2018-08-26T04:46:25", Steps:400 }, { DateMeasured:"2018-08-26T04:46:25", Steps:300 }, { DateMeasured:"2018-08-25T04:46:25", Steps:100 } ];
const sorted = data.sort((a, b) => b.Steps - a.Steps)
const takeFirst3 = sorted.slice(0, 3)
console.log(takeFirst3)

Split array into chunk of different size on the basis of their attribute

I have this following array
var array=[{ semster:1, name:Book1 }, { semster:1, name:Book2 }, { semster:2, name:Book4 }, { semster:3, name:Book5 }, { semster:3, name:Book6 }, { semster:4, name:Book7 }]
Now I want to sort my array to split the current array into chunks of array like following
var array=[[{ semster:1, name:Book1 }, { semster:1, name:Book2 }],[ { semster:2, name:Book4 }], [{ semster:3, name:Book5 }, { semster:3, name:Book6 }], [{ semster:4, name:Book7 }]]
I have tried to achieve this with following code :
function splitIntoSubArray(arr, count) {
var newArray = [];
while (arr.length > 0) {
newArray.push(arr.splice(0, count));
}
return newArray;
}
But this can only divide the array on the basis of fixed size. Any kind of suggestion is appreciated.
Thanks
You can simply use Array.reduce() to group items by semester. Object.values() on the map gives you the desired result.
var array=[{ semster:1, name:"Book1" }, { semster:1, name:"Book2" }, { semster:2, name:"Book4" }, { semster:3, name:"Book5" }, { semster:3, name:"Book6" }, { semster:4, name:"Book7" }];
var result = Object.values(array.reduce((a, curr)=>{
(a[curr.semster] = a[curr.semster] || []).push(curr);
return a;
},{}));
console.log(result);
You could reduce the array by checking the last group with the same semester.
var array = [{ semester: 1, name: 'Book1' }, { semester: 1, name: 'Book2' }, { semester: 2, name: 'Book4' }, { semester: 3, name: 'Book5' }, { semester: 3, name: 'Book6' }, { semester: 4, name: 'Book7' }],
grouped = array.reduce((r, o) => {
var last = r[r.length - 1];
if (last && last[0].semester === o.semester) {
last.push(o);
} else {
r.push([o]);
}
return r;
}, []);
console.log(grouped);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Categories