Joining 2 JSON arrays, only taking the matches - javascript

I have 2 JSON arrays, both of which can contain nested arrays:
var serverArr = [
{ "id": 1, "text": "Item 1" },
{ "id": 2, "text": "Item 2" },
{ "id": 3, "text": "Item 3", "children": [{ "id": 20, "text": "Item 20" },
{ "id": 21, "text": "Item 21" }] },
{ "id": 4, "text": "Item 4" }
];
var userArr = [
{ "id": 1, "text": "Item 1" },
{ "id": 3, "text": "Item 3", "children": [{ "id": 20, "text": "Item 20" },
{ "id": 25, "text": "Item 25" }] },
{ "id": 5, "text": "Item 5" }
];
What I need to do is combine them into 1 array, only taking the matching values. So the result should look like:
[{ "id": 1, "text": "Item 1" },
{ "id": 3, "text": "Item 3", "children": [{ "id": 20, "text": "Item 20" }]}];
I'm using the results with the jsTree plugin, so the format has to be like this unfortunately.
How can I get the end result from those 2 arrays?

I have defined a function that does 3 things to solve this problem
First thing it gets all the elements that are in both arrays and it collects all childs for every item in both arrays
Second thing is filtering all the childs elements to keep only unique ones
Third thing is changing the children of the parent elements to keep only unique ones
function combine(array1,array2)
{
result = serverArr.concat(userArr);
var childs = [];
// First step
result = result.filter(function(elem, index, self)
{
if(elem.children != undefined)
childs[elem.id] = ((childs[elem.id] == undefined) ? [] : childs[elem.id]).concat(elem.children);
for (key in self)
if(key < index && elem.id == self[key].id )
return true;
return false;
});
// Second step
for(i in childs)
childs[i] = childs[i].filter(function(elem, index, self)
{
for (key in self)
if(key < index && JSON.stringify(elem) == JSON.stringify(self[key]) )
return true;
return false;
});
//Third step
for(key in result)
if(childs[result[key].id] != undefined)
result[key].children = childs[result[key].id];
return result;
}

Related

Splitting object of nth length into one array

I'm given an Object of n length, with x amount of arrays in the object. I would like not to include the 0's, and to be sorted in descending order.
I've made a simple solution, that takes the value in each object, and takes the nth value and puts that in an array, but am looking for something more robust and shorter; any help would be helpful!
const arr = {
"rows": [{
"value": "demo value 1",
"data": [15, 45, 0, 0]
}, {
"value": "demo value 2",
"data": [11, 87, 0, 0]
}, {
"value": "demo value 3",
"data": [8, 113, 0, 0]
}, {
"value": "demo value 4",
"data": [7, 26, 0, 2]
}, {
"value": "demo value 5",
"data": [7, 3, 0, 0]
}, {
"value": "demo value 6",
"data": [6, 17, 0, 1]
}]
};
let newArr = [];
let newArr2 = [];
let newArr3 = [];
let newArr4 = [];
for (i = 0; i < arr.rows.length; i++) {
if (arr.rows[i].data[0] != 0)
newArr.push({
value: arr.rows[i].value,
data: arr.rows[i].data[0]
})
if (arr.rows[i].data[1] != 0)
newArr2.push({
value: arr.rows[i].value,
data: arr.rows[i].data[1]
})
if (arr.rows[i].data[2] != 0)
newArr3.push({
value: arr.rows[i].value,
data: arr.rows[i].data[2]
})
if (arr.rows[i].data[3] != 0)
newArr4.push({
value: arr.rows[i].value,
data: arr.rows[i].data[3]
})
/// ... and so on
}
console.log(newArr)
console.log(newArr2)
console.log(newArr3)
console.log(newArr4)
EDIT:
Expected result:
[
[
{
"value": "demo value 1",
"data": 15
},
{
"value": "demo value 2",
"data": 11
},
{
"value": "demo value 3",
"data": 8
},
{
"value": "demo value 4",
"data": 7
},
{
"value": "demo value 5",
"data": 7
},
{
"value": "demo value 6",
"data": 6
}
],
[
{
"value": "demo value 3",
"data": 113
},
{
"value": "demo value 2",
"data": 87
},
{
"value": "demo value 1",
"data": 45
},
{
"value": "demo value 4",
"data": 26
},
{
"value": "demo value 6",
"data": 17
},
{
"value": "demo value 5",
"data": 3
}
],
[],
[
{
"value": "demo value 4",
"data": 2
},
{
"value": "demo value 6",
"data": 1
}
]
]
I'm not quite sure if this is what you're after as there is no "expected format" provided - however this may put you on the right track
let new_arrays = [];
for (let i = 0; i < arr.rows.length; i++) {
let current_row = arr.rows[i];
for (let j = 0; j < current_row.data.length; j++) {
let current_data = current_row.data[j];
if(current_data === 0){
continue;
}
if(new_arrays[j] === undefined){
new_arrays[j] = [];
}
new_arrays[j].push({
value: current_row.value,
data : current_data,
});
}
}
console.log(new_arrays);
NOTE: if all values at an index are 0 (or the index doesn't exist), there will be no "new array" at that index

How to remove items from an array of multidimensional arrays

I need some help on how to remove items from a TreeView (it's a Vue.js project), the TreeView is build based on an element like that:
[
{
"id": 1,
"name": "COMERCIALIZAÇÃO",
"idp": "",
"children": [
{
"id": 5,
"name": "Pasta 1",
"idp": 1,
"children": [
{
"id": 6,
"name": "Pasta 1 2",
"idp": 5,
"children": [
{
"id": 7,
"name": "NO.FT.DRC.01.00.001.pdf",
"file": "pdf",
"idp": 6
},
{
"id": 8,
"name": "PR.FT.DRC.01.00.003.pdf",
"file": "pdf",
"idp": 6
}
]
},
{
"id": 9,
"name": "imprimir p luiza.pdf",
"file": "pdf",
"idp": 5
},
{
"id": 66,
"name": "Pasta 1 3",
"idp": 5,
"children": [
{
"id": 77,
"name": "NO.FT.DRC.01.00.001.pdf",
"file": "pdf",
"idp": 66
},
{
"id": 88,
"name": "PR.FT.DRC.01.00.003.pdf",
"file": "pdf",
"idp": 66
}
]
}
]
},
{
"id": 10,
"name": "Backend.docx",
"file": "pdf",
"idp": 1
},
{
"id": 0,
"name": "DT.DC.RPI.03.03.1235_V2.docx",
"file": "pdf",
"idp": 1
}
]
},
{
"id": 2,
"name": "DISTRIBUIÇÃO",
"idp": "",
"children": [
{
"id": 11,
"name": "Pasta 2",
"idp": 2,
"children": [
{
"id": 12,
"name": "pasta 2 1",
"idp": 11,
"children": [
{
"id": 13,
"name": "script.sql",
"file": "pdf",
"idp": 12
}
]
}
]
}
]
},
{
"id": 3,
"name": "GERAÇÃO",
"idp": "",
"children": [
{
"id": 14,
"name": "Pasta 3",
"idp": 3
}
]
},
{
"id": 4,
"name": "SERVIÇOS",
"idp": "",
"children": [
{
"id": 5,
"name": "teste",
"idp": 4
}
]
}
]
I'm not sure, but I think that the best way to describe that element is: array of mutidimensional arrays, right?
I've created a CodePen to show the closest I got when using recursivity, but surely mine isn't the best solution since it doesn't work on every delete. Take a look at my code: https://codepen.io/luizarusso/pen/zYxLOPb?editors=1010
for (let i = 0; i < items.length; i++) {
if (items[i].id == item.id) {
//se achou o cara que vai ser removido, chama a função de remover
return this.removeItem(i);
} else {
if (items[i].children) {
if (items[i].idp == "") {
this.caminho = [];
}
this.caminho.push(i);
this.delFile(item, items[i].children);
} else {
if (items.length == 1 + i) {
this.caminho.pop();
}
}
}
}
Any ideas? Feel free to optimize my code directly on CodePen if you prefer :)
EDIT: Just to clarify, my problem here is strictly on how to remove an element by the id. When the user clicks on the bin icon I know what element I need to remove, but I don't know how to take it off of the array. Map, Filter and other native JS functions cannot do that to an array of arrays/JSON, so I tought about using recursivity or something else to make it work.
You need to look at objects, not just arrays.
Let me recommend an example library. https://github.com/leezng/vue-json-pretty.
If your question about multidimensional array iteration and process i think you have to ask on javascript and/or algorithm tags.
I hope this answer will help you.
The problem was with where I placed the this.caminho.pop()
I should only do that in the "else" of the condition that compares the id of the current item with the id of the item I'm looking for.
delFile(item, items) {
for (let i = 0; i < items.length; i++) {
if (items[i].id == item.id) {
//if the current item has the same id as the item I'm looking for
//it means I found the guy and I call the function to remove it
return this.removeItem(i);
} else {
//otherwise, I keep on searching
if (items[i].children) {
//if the item on the actual index have children, I'll search among them
if (items[i].idp == "") {
//if the items doesn't have a parent, I clean the "caminho" (path) var. That var traces the route till the item I'm looking for
this.caminho = [];
}
//I push the index to the var that traces the route
this.caminho.push(i);
//I call the function back again, now with the child items
this.delFile(item, items[i].children);
}
if (items.length == 1 + i) {
//if the item's lenght has been completely coursed, I pop the index out of the var that holds the route, because at this point I know the item I'm looking for is not among them
this.caminho.pop()
}
}
}
},
Here is the solution: https://codepen.io/luizarusso/pen/zYxLOPb
Works with treeview with any deepness

Extracting array from nested object properties by nest level

I have the following JSON of structured, nested data.
[
{
"id": "2e270ad7-90aa-41da-bb57-a777448f5906",
"name": "First Level 1",
"childValues": [
{
"id": "4cecbd28-fd06-4c2a-9b57-33d4a298675c",
"name": "Second Level 1 "
},
{
"id": "09893799-e21c-498f-96b4-e63e366a3c18",
"name": "Second Level 2"
}
]
},
{
"id": "18889675-9d71-420e-84a6-3603af044b6c",
"name": "First Level 2",
"childValues": [
{
"id": "b7093ca1-5fed-4eb2-b934-637bfdc6c7da",
"name": "Second Level 3"
},
{
"id": "a3575212-1746-4dd3-ab52-4e37786c035c",
"name": "Second Level 4"
}
]
},
{
"id": "71113ffb-62f0-4d76-941f-974be3cd35cb",
"name": "First Level 3",
"childValues": [
{
"id": "160570a5-29aa-4fdb-bb16-d9d7637d0177",
"name": "Second Level 5",
"childValues": [
{
"id": "2df28cb9-9ac4-478c-a2a4-6dc5206c983b",
"name": "Third Level 1"
},
{
"id": "a974cfac-1e2c-461c-ab64-0f5dd9d1cf1e",
"name": "Third Level 2"
}
]
},
{
"id": "6e5947ea-2c47-4d2b-8ecd-6369c728e7db",
"name": "Second Level 6"
}
]
}
]
I am trying to extract an array of objects from this nested array structure based on a level of nesting. For instance, level 0 just gives me back all of the base object in the array, but if I ask for level 1, I am trying to get back an output of just the second level nested objects, under the childValues property, in a single array, like the following:
[
{
"id": "4cecbd28-fd06-4c2a-9b57-33d4a298675c",
"name": "Second Level 1 "
},
{
"id": "09893799-e21c-498f-96b4-e63e366a3c18",
"name": "Second Level 2"
},
{
"id": "b7093ca1-5fed-4eb2-b934-637bfdc6c7da",
"name": "Second Level 3"
},
{
"id": "a3575212-1746-4dd3-ab52-4e37786c035c",
"name": "Second Level 4"
},
{
"id": "160570a5-29aa-4fdb-bb16-d9d7637d0177",
"name": "Second Level 5",
"childValues": [
{
"id": "2df28cb9-9ac4-478c-a2a4-6dc5206c983b",
"name": "Third Level 1"
},
{
"id": "a974cfac-1e2c-461c-ab64-0f5dd9d1cf1e",
"name": "Third Level 2"
}
]
},
{
"id": "6e5947ea-2c47-4d2b-8ecd-6369c728e7db",
"name": "Second Level 6"
}
]
And if I ask for level 2, I should only get the third level objects:
[
{
"id": "2df28cb9-9ac4-478c-a2a4-6dc5206c983b",
"name": "Third Level 1"
},
{
"id": "a974cfac-1e2c-461c-ab64-0f5dd9d1cf1e",
"name": "Third Level 2"
}
]
The only thing I have figured out is how to completely flatten the structure recursively, but cant put my finger on how to extract a specific level.
private flat(array: any[]) {
let result: any[] = [];
array.forEach((a) => {
result.push(a);
if (Array.isArray(a.childValues)) {
result = result.concat(this.flat(a.childValues));
}
});
return result;
}
Here's a cleaner version of Shane Padgett's function:
const getArrayByNthLevel = (array, levelToGet, currentLevel=0) => array.reduce((retval, a) => {
levelToGet === currentLevel
? retval.push(a)
: Array.isArray(a.childValues)
? retval = retval.concat(getArrayByNthLevel(a.childValues, levelToGet, currentLevel + 1))
: false
return retval;
}, []);
I was able to achieve this with the following function.
function getArrayByNthLevelOfPropName(array, propName, levelToGet, currentLevel = 0) {
let result = [];
array.forEach((a) => {
if (levelToGet === currentLevel) {
result.push(a);
}
if (Array.isArray(a[propName]) && levelToGet !== currentLevel) {
result = result.concat(getArrayByNthLevelOfPropName(a[propName], propName, levelToGet, ++currentLevel));
currentLevel -= 1;
}
});
return result;
}
You can use flatMap like this:
const input=[{id:"2e270ad7-90aa-41da-bb57-a777448f5906",name:"First Level 1",childValues:[{id:"4cecbd28-fd06-4c2a-9b57-33d4a298675c",name:"Second Level 1 "},{id:"09893799-e21c-498f-96b4-e63e366a3c18",name:"Second Level 2"}]},{id:"18889675-9d71-420e-84a6-3603af044b6c",name:"First Level 2",childValues:[{id:"b7093ca1-5fed-4eb2-b934-637bfdc6c7da",name:"Second Level 3"},{id:"a3575212-1746-4dd3-ab52-4e37786c035c",name:"Second Level 4"}]},{id:"71113ffb-62f0-4d76-941f-974be3cd35cb",name:"First Level 3",childValues:[{id:"160570a5-29aa-4fdb-bb16-d9d7637d0177",name:"Second Level 5",childValues:[{id:"2df28cb9-9ac4-478c-a2a4-6dc5206c983b",name:"Third Level 1"},{id:"a974cfac-1e2c-461c-ab64-0f5dd9d1cf1e",name:"Third Level 2"}]},{id:"6e5947ea-2c47-4d2b-8ecd-6369c728e7db",name:"Second Level 6"}]}];
const getLevel = (arr = [], required, current = 0) =>
required === current
? arr
: arr.flatMap(a => getLevel(a.childValues, required, current + 1))
console.log("Second Level: \n ", getLevel(input, 1))
console.log("Third Level: \n ", getLevel(input, 2))
If flatMap is not supported, you can use
[].concat(...arr.map(a => getLevel(a.childValues, required, current + 1)))
Following should work:
var data = [
{
"id": "2e270ad7-90aa-41da-bb57-a777448f5906",
"name": "First Level 1",
"childValues": [
{
"id": "4cecbd28-fd06-4c2a-9b57-33d4a298675c",
"name": "Second Level 1"
},
{
"id": "09893799-e21c-498f-96b4-e63e366a3c18",
"name": "Second Level 2"
}
]
},
{
"id": "18889675-9d71-420e-84a6-3603af044b6c",
"name": "First Level 2",
"childValues": [
{
"id": "b7093ca1-5fed-4eb2-b934-637bfdc6c7da",
"name": "Second Level 3"
},
{
"id": "a3575212-1746-4dd3-ab52-4e37786c035c",
"name": "Second Level 4"
}
]
},
{
"id": "71113ffb-62f0-4d76-941f-974be3cd35cb",
"name": "First Level 3",
"childValues": [
{
"id": "160570a5-29aa-4fdb-bb16-d9d7637d0177",
"name": "Second Level 5",
"childValues": [
{
"id": "2df28cb9-9ac4-478c-a2a4-6dc5206c983b",
"name": "Third Level 1"
},
{
"id": "a974cfac-1e2c-461c-ab64-0f5dd9d1cf1e",
"name": "Third Level 2"
}
]
},
{
"id": "6e5947ea-2c47-4d2b-8ecd-6369c728e7db",
"name": "Second Level 6"
}
]
}
];
function getData(data, targetLevel, currentLevel = 0) {
// If this is the target level, then extract the
// data we need from each item, and return the array
if (currentLevel == targetLevel) {
return data;
}
// Otherwise, run a map over the items, and if they have
// 'childValues', then recurs, but increment the value of
// 'current level' it will be iterating on
// Because 'map' will return array of array, merge them
// to a single array
return [].concat(...data.map(item => {
if (item.childValues) {
return getData(item.childValues, targetLevel, currentLevel + 1);
};
return [];
}));
}
document.getElementById("dataLevel0").innerHTML = JSON.stringify(getData(data, 0), null, 4);
document.getElementById("dataLevel1").innerHTML = JSON.stringify(getData(data, 1), null, 4);
document.getElementById("dataLevel2").innerHTML = JSON.stringify(getData(data, 2), null, 4);
<div>
Level 1:
</div>
<pre id="dataLevel0">
</pre>
<div>
Level 2:
</div>
<pre id="dataLevel1">
</pre>
<div>
Level 3:
</div>
<pre id="dataLevel2">
</pre>

Javascript create 2 arrays from object data

I have some data and I need a loop which creates 2 arrays...
So I first create the 2 arrays:
namelist = [];
countList = [];
{
"id": "622",
"name": "main",
"sub": {
"637": {
"id": "637",
"name": "name 1",
"stats": {
"count": 5
}
},
"638": {
"id": "638",
"name": "name 2",
"stats": {
"count": 10
}
}
}
}
The desired result for this example would be:
For namelist:
['name 1', 'name 2']
For countList:
[5, 10]
How can I do this?
var nameList = [];
var countList = [];
var myObj =
{
"id": "622",
"name": "main",
"sub": {
"637": {
"id": "637",
"name": "name 1",
"stats": {
"count": 5
}
},
"638": {
"id": "638",
"name": "name 2",
"stats": {
"count": 10
}
}
}
};
for(var key in myObj.sub){
nameList.push(myObj.sub[key].name);
countList.push(myObj.sub[key].stats.count);
}
console.log(nameList);
console.log(countList);
for(var key in obj.sub){
nameList.push(obj.sub[key].name);
countList.push(obj.sub[key].stats.count;
}
Object.keys may help you to walk through object properties. Example related to your object:
var namelist = [],
countList = [],
obj = {
"id": "622",
"name": "main",
"sub": {
"637": {
"id": "637",
"name": "name 1",
"stats": {
"count": 5
}
},
"638": {
"id": "638",
"name": "name 2",
"stats": {
"count": 10
}
}
}
};
Object.keys(obj.sub).forEach(function(item) {
namelist.push(obj.sub[item].name);
countList.push(obj.sub[item].stats.count);
});
console.log(namelist, countList);
Working example: https://jsfiddle.net/ry0zqweL/
Obviously, you can optimise it in many ways. It's just illustrating one of the many solutions.

Get all children from parent child JSON data

I have parent-child JSON data and I want get all children (nested children) from selected parent.
For example, I have JSON data :
[{
"id": 1,
"parent": 0,
"name": "Parent"
}, {
"id": 2,
"parent": 1,
"name": "Child 1"
}, {
"id": 3,
"parent": 2,
"name": "Grand Child 1"
}, {
"id": 4,
"parent": 2,
"name": "Grand Child 2"
}, {
"id": 5,
"parent": 1,
"name": "Child 2"
}]
And I have function findAllChildren(1), where "1" is "parent" and then result of function should be :
[{
"id": 2,
"parent": 1,
"name": "Child 1"
}, {
"id": 3,
"parent": 2,
"name": "Grand Child 1"
}, {
"id": 4,
"parent": 2,
"name": "Grand Child 2"
}, {
"id": 5,
"parent": 1,
"name": "Child 2"
}]
And in other case, if i call findAllChildren(2), result of the function should like below :
[{
"id": 3,
"parent": 2,
"name": "Grand Child 1"
}, {
"id": 4,
"parent": 2,
"name": "Grand Child 2"
}]
What is the proper way to create function to solve that case? Thank you.
You can just iterate over the original data and look for items that has the specified id as parent_id. If found, do the same recursively with the element's id.
Check it out here: https://jsfiddle.net/6ydog1tj/2/
function findAllChildren (id, results, depth) {
for (d in data) {
if (data[d].parent == id) {
data[d].depth = depth
results.push(data[d])
findAllChildren(data[d].id, results, depth + 1)
}
}
}
var results = []
findAllChildren(1, results, 0)
$('body').append(results.map(function (element) { return Array(element.depth + 1).join(' -> ') + element.name + '<br>' }))
console.log(results)
prints out
Child 1
-> Grand Child 1
-> Grand Child 2
Child 2
I suggest to iterate all data and build a tree like object with properties to start the search with all given id.
Then the object is walked and the children iterated for the result.
function getDescendant(id) {
var result = [];
Array.isArray(object[id].children) && object[id].children.forEach(function iter(a) {
result.push({ id: a.id, parent: a.parent, name: a.name });
Array.isArray(a.children) && a.children.forEach(iter);
});
return result;
}
var data = [{ id: 1, parent: 0, name: "Parent" }, { id: 2, parent: 1, name: "Child 1" }, { id: 3, parent: 2, name: "Grand Child 1" }, { id: 4, parent: 2, name: "Grand Child 2" }, { id: 5, parent: 1, name: "Child 2" }],
object = function (data, root) {
var o = {};
data.forEach(function (a) {
a.children = o[a.id] && o[a.id].children;
o[a.id] = a;
o[a.parent] = o[a.parent] || {};
o[a.parent].children = o[a.parent].children || [];
o[a.parent].children.push(a);
});
return o;
}(data, 0);
console.log(getDescendant(1));
console.log(getDescendant(2));
console.log(object);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can use Array.prototype.filter to remove items from an array that do not match a predicate condition.
filter will loop over an array and run a function for each iteration. it the return value is true the item will be in the returned array.
The parentId function passed into filter is curried. it will lock in the parent id you are searching for in the scope and return the function that filter will run.
const data = [{
"id": 1,
"parent": 0,
"name": "Parent"
}, {
"id": 2,
"parent": 1,
"name": "Child 1"
}, {
"id": 3,
"parent": 2,
"name": "Grand Child 1"
}, {
"id": 4,
"parent": 2,
"name": "Grand Child 2"
}, {
"id": 5,
"parent": 1,
"name": "Child 2"
}]
function parentId(id) {
return function(item) {
return item.parent === id
}
}
console.log(
data.filter(parentId(2))
)

Categories