I want to parse a json that is fetch from api
I have following schema
nodes = [
{
id: 1,
name: 'root1',
key: 'value1'
key1: 'value2'
children: [
{ id: 2, name: 'child1' },
{ id: 3, name: 'child2' }
]
},
{
id: 4,
name: 'root2',
key3: 'value3'
key4: 'value4'
children: [
{ id: 5, name: 'child2.1' },
{
id: 6,
name: 'child2.2',
key5: 'value5'
key6: 'value6'
children: [{
id: 7,
name: 'subsub',
children: [{
id: 8,
name: 'subsubsub'
}]
}]
}
]
}
];
I'm trying following but not working
data = []
var nodesFun = function(n, data, cObj) {
let obj = {}
obj["name"] = n['name']
if (n['children']) {
console.log(obj);
console.log("name--> "+n['name']);
let childList = []
for (let i=0; i < n['children'].length; i++){
console.log("cname--> "+n['children'][i]['name']);
childList.push({"name": n['children'][i]['name']})
let dataObj = nodesFun(n['children'][i], data, obj)
if (dataObj){
data.push(dataObj)
}
}
obj["children"] = childList
cObj["children"] = obj
return cObj
}else{
cObj["children"] = obj
return cObj
}
}
nodesFun(nodes, data, {})
console.log(nodes);
Now I want to convert above json in following format using recursive function
nodes = [{
id: 1,
name: 'root1',
children: [
{ id: 2, name: 'child1' },
{ id: 3, name: 'child2' }
]
},
{
id: 4,
name: 'root2',
children: [
{ id: 5, name: 'child2.1' },
{
id: 6,
name: 'child2.2',
children: [{
id: 7,
name: 'subsub',
children: [{
id: 8,
name: 'subsubsub'
}]
}]
}
]
}
];
You can modify your original nodes array using array#forEach and recursive approach. Iterate through each key of node if the key includes key word delete it and if it contains children call deleteKey function again.
var nodes = [ { id: 1, name: 'root1', key: 'value1', key1: 'value2', children: [ { id: 2, name: 'child1' }, { id: 3, name: 'child2' } ] }, { id: 4, name: 'root2', key3: 'value3', key4: 'value4', children: [ { id: 5, name: 'child2.1' }, { id: 6, name: 'child2.2',key5: 'value5', key6: 'value6', children: [{ id: 7, name: 'subsub', children: [{ id: 8, name: 'subsubsub' }] }] } ] } ];
var deleteKey = (nodes) => {
nodes.forEach(node => {
Object.keys(node).forEach(k => {
if(k.includes('key'))
delete node[k];
if(k == 'children')
deleteKey(node[k]);
})
});
}
deleteKey(nodes);
console.log(nodes);
.as-console-wrapper { max-height: 100% !important; top: 0; }
In case you want a new array, you can use array#reduce and array#map.
var nodes = [ { id: 1, name: 'root1', key: 'value1', key1: 'value2', children: [ { id: 2, name: 'child1' }, { id: 3, name: 'child2' } ] }, { id: 4, name: 'root2', key3: 'value3', key4: 'value4', children: [ { id: 5, name: 'child2.1' }, { id: 6, name: 'child2.2',key5: 'value5', key6: 'value6', children: [{ id: 7, name: 'subsub', children: [{ id: 8, name: 'subsubsub' }] }] } ] } ];
var removeKey = (nodes) => {
return nodes.map(node => {
return Object.keys(node).reduce((r, k) => {
if(!k.includes('key'))
r[k] = node[k];
if(k == 'children')
r[k] = removeKey(node[k]);
return r;
},{});
});
}
console.log(removeKey(nodes));
.as-console-wrapper { max-height: 100% !important; top: 0; }
The function delete all the keys that are not id, name and children from your object
let nodes = [
{
id: 1,
name: 'root1',
key: 'value1',
key1: 'value2',
children: [
{ id: 2, name: 'child1' },
{ id: 3, name: 'child2' }
]
},
{
id: 4,
name: 'root2',
key3: 'value3',
key4: 'value4',
children: [
{ id: 5, name: 'child2.1' },
{
id: 6,
name: 'child2.2',
key5: 'value5',
key6: 'value6',
children: [{
id: 7,
name: 'subsub',
children: [{
id: 8,
name: 'subsubsub'
}]
}]
}
]
}
];
const getFinalNode = function(arr){
arr.forEach(function(obj, idx){
let tmpObj = {
id: obj.id,
name: obj.name,
}
if(obj.children && obj.children.length){
tmpObj.children = getFinalNode(obj.children)
}
arr[idx] = tmpObj
})
return arr
}
getFinalNode(nodes);
console.log(nodes)
Related
My nested json array looks like:
[
{
id: 1,
name: "Mike",
children: [
{ id: 2, name: "MikeC1" },
{ id: 3, name: "MikeC2" },
{
id: 4, name: "MikeC3",
children: [{ id: 5, name: "MikeCC1" }]
},
]
},
{
id: 6,
name: "Json",
children: [
{ id: 7, name: "JsonC1" },
{ id: 8, name: "JsonC2" },
{
id: 9, name: "JsonC3",
children: [{ id: 10, name: "JsonCC1" },{ id: 11, name: "JsonCC2" }]
},
]
}
]
Now I get a id like "11"
then get the parent ids array in json like [6,9,11]
How to do?
var id = 11
console.log(findParent(id))
//result is [6,9,11]
You need to do recursive search
const persons = [
{
id: 1,
name: "Mike",
children: [
{ id: 2, name: "MikeC1" },
{ id: 3, name: "MikeC2" },
{
id: 4, name: "MikeC3",
children: [{ id: 5, name: "MikeCC1" }]
},
]
},
{
id: 6,
name: "Json",
children: [
{ id: 7, name: "JsonC1" },
{ id: 8, name: "JsonC2" },
{
id: 9, name: "JsonC3",
children: [{ id: 10, name: "JsonCC1" },{ id: 11, name: "JsonCC2" }]
},
]
}
];
function searchRecursive(items, id) {
const allIds = [];
items.forEach(item => {
if(item.id === id) {
allIds.push(item.id);
}
else if(item.children) {
const ids = searchRecursive(item.children, id);
if(ids.length) allIds.push(item.id);
ids.forEach(id => allIds.push(id));
}
});
return allIds;
}
console.log(searchRecursive(persons, 11));
I have an array:
[
{ id: 1,
name: "parent1",
children: [
{ id: 10,
name: "first_child_of_id_1",
children: [
{ id: 100, name: "child_of_id_10", children: []},
{ id: 141, name: "child_of_id_10", children: []},
{ id: 155, name: "child_of_id_10", children: []}
]
},
{ id: 42,
name: "second_child_of_id_1",
children: [
{ id: 122, name: "child_of_id_42", children: []},
{ id: 133, name: "child_of_id_42", children: []},
{ id: 177, name: "child_of_id_42", children: []}
]
}
]
},
{ id: 7,
name: "parent7",
children: [
{ id: 74,
name: "first_child_of_id_7",
children: [
{ id: 700, name: "child_of_id_74", children: []},
{ id: 732, name: "child_of_id_74", children: []},
{ id: 755, name: "child_of_id_74", children: []}
]
},
{ id: 80,
name: "second_child_of_id_7",
children: [
{ id: 22, name: "child_of_id_80", children: []},
{ id: 33, name: "child_of_id_80", children: []},
{ id: 77, name: "child_of_id_80", children: []}
]
}
]
}
]
What I need is an array of arrays like this:
[
[ "id", "name", "parent_id", "parent_name" ],
[ 1, "parent1", null, "" ],
[ 10, "first_child_of_id_1", 1, "parent1"],
[ 42, "second_child_of_id_1", 1, "parent1"],
[100, "child_of_id_10", 10, "first_child_of_id_1"]
]
and so on for all nested objects for me to convert them into CSV rows. I've checked many answers and found a similar problem here: How to convert array of nested objects to CSV?
But it produces too long rows for many nested objects and I am not experienced enough with JavaScript to modify map function.
const categories = [
{ id: 1,
name: "parent1",
children: [
{ id: 10,
name: "first_child_of_id_1",
children: [
{ id: 100, name: "child_of_id_10", children: []},
{ id: 141, name: "child_of_id_10", children: []},
{ id: 155, name: "child_of_id_10", children: []}
]
},
{ id: 42,
name: "second_child_of_id_1",
children: [
{ id: 122, name: "child_of_id_42", children: []},
{ id: 133, name: "child_of_id_42", children: []},
{ id: 177, name: "child_of_id_42", children: []}
]
}
]
},
{ id: 7,
name: "parent7",
children: [
{ id: 74,
name: "first_child_of_id_7",
children: [
{ id: 700, name: "child_of_id_74", children: []},
{ id: 732, name: "child_of_id_74", children: []},
{ id: 755, name: "child_of_id_74", children: []}
]
},
{ id: 80,
name: "second_child_of_id_7",
children: [
{ id: 22, name: "child_of_id_80", children: []},
{ id: 33, name: "child_of_id_80", children: []},
{ id: 77, name: "child_of_id_80", children: []}
]
}
]
}
]
function pivot(arr) {
var mp = new Map();
function setValue(a, path, val) {
if (Object(val) !== val) { // primitive value
var pathStr = path.join('.');
var i = (mp.has(pathStr) ? mp : mp.set(pathStr, mp.size)).get(pathStr);
a[i] = val;
} else {
for (var key in val) {
setValue(a, key == '0' ? path : path.concat(key), val[key]);
}
}
return a;
}
var result = arr.map(obj => setValue([], [], obj));
return [[...mp.keys()], ...result];
}
function toCsv(arr) {
return arr.map(row =>
row.map(val => isNaN(val) ? JSON.stringify(val) : +val).join(',')
).join('\n');
}
<button onclick="console.log(toCsv(pivot(categories)))">Output</button>
Simple DFS or BFS algorithm should do the job here.
The difference is the order of created "rows". If you want to have all children of given node listed immediately after their parent, then you need to use BFS.
Example with DFS and BFS:
const input = [{
id: 1,
name: "parent1",
children: [{
id: 10,
name: "first_child_of_id_1",
children: [{
id: 100,
name: "child_of_id_10",
children: []
},
{
id: 141,
name: "child_of_id_10",
children: []
},
{
id: 155,
name: "child_of_id_10",
children: []
}
]
},
{
id: 42,
name: "second_child_of_id_1",
children: [{
id: 122,
name: "child_of_id_42",
children: []
},
{
id: 133,
name: "child_of_id_42",
children: []
},
{
id: 177,
name: "child_of_id_42",
children: []
}
]
}
]
},
{
id: 7,
name: "parent7",
children: [{
id: 74,
name: "first_child_of_id_7",
children: [{
id: 700,
name: "child_of_id_74",
children: []
},
{
id: 732,
name: "child_of_id_74",
children: []
},
{
id: 755,
name: "child_of_id_74",
children: []
}
]
},
{
id: 80,
name: "second_child_of_id_1",
children: [{
id: 22,
name: "child_of_id_80",
children: []
},
{
id: 33,
name: "child_of_id_80",
children: []
},
{
id: 77,
name: "child_of_id_80",
children: []
}
]
}
]
}
]
//DFS
function deepWalk(node, parent, output = []) {
if (!node || typeof node !== 'object' || !node.id) return;
output.push([node.id, node.name, parent ? parent.id : null, parent ? parent.name : ""])
if (node.children) {
for (const child of node.children) {
deepWalk(child, node, output);
}
}
return output;
}
//BFS
function broadWalk(root) {
const output = []
const queue = [];
queue.push({
node: root,
parent: null
});
while (queue.length) {
const {
node,
parent
} = queue.shift();
output.push([node.id, node.name, parent ? parent.id : null, parent ? parent.name : ""])
if (node.children) {
for (const child of node.children) {
queue.push({
node: child,
parent: node
});
}
}
}
return output;
}
let rowsDfs = [
["id", "name", "parent_id", "parent_name"]
];
let rowsBfs = [
["id", "name", "parent_id", "parent_name"]
];
for (const node of input) {
rowsDfs = [...rowsDfs, ...deepWalk(node)];
rowsBfs = [...rowsBfs, ...broadWalk(node)];
}
console.log("rows DFS: ", rowsDfs)
console.log("rows BFS: ", rowsBfs)
I want to keep the parent-child relationship of the tree node.
I have a JSON tree, like this
{
id: null,
children: [
{
id: 1,
children: [
{
id: 11,
children: [
{
id: 111
children: []
}
]
},
{
id: '12',
children: []
},
]
},
{
id: '2',
children: [
{
id: '21',
children: []
},
{
id: '22',
children: [
{
id: '221',
children: []
}
]
},
]
},
]
}
I want flat the tree, like this
[
{ id: 1, parent: null,},
{ id: 11, parent: 1, },
{ id: 111, parent: 11, },
{ id: 2, parent: null, },
{ id: 21, parent: 2, },
...
]
parent automatic generated
Is there any good way?
You could use flatMap method and create recursive function that will return 1D array as a result.
const data = {"id":null,"children":[{"id":1,"children":[{"id":11,"children":[{"id":111,"children":[]}]},{"id":"12","children":[]}]},{"id":"2","children":[{"id":"21","children":[]},{"id":"22","children":[{"id":"221","children":[]}]}]}]}
const flatten = (data, parent = null) =>
data.flatMap(({ id, children }) => ([
{ id, parent },
...flatten(children, id)
]))
const result = flatten(data.children);
console.log(result)
You could get the object and return an array of the flat children.
const
getFlat = ({ id, children = [] }) =>
children.flatMap(o => [{ id: o.id, parent: id }, ...getFlat(o)]);
var data = { id: null, children: [{ id: 1, children: [{ id: 11, children: [{ id: 111, children: [] }] }, { id: '12', children: [] }] }, { id: '2', children: [{ id: '21', children: [] }, { id: '22', children: [{ id: '221', children: [] }] }] }] },
flat = getFlat(data);
console.log(flat);
.as-console-wrapper { max-height: 100% !important; top: 0; }
If compatbility with Internet Explorer is important, then the following approach which avoids the need for Array#flatMap might suit:
const input={id:null,children:[{id:'1',children:[{id:'11',children:[{id:'111',children:[]}]},{id:'12',children:[]}]},{id:'2',children:[{id:'21',children:[]},{id:'22',children:[{id:'221',children:[]}]}]}]};
const result = [];
// Define recursive function to walk through the data tree
// and produce a flat array of required data
const recurse = (item, parent) => {
// Only add item to result if it has valid id
if (item.id) {
result.push({
id: item.id,
parent: parent ? parent.id : null
});
}
// Iterate the children of this item, traversing and
// processing them in the same way
item.children.forEach(child => recurse(child, item))
}
recurse(input);
console.log(result);
You can use a simple recursion to achieve this
let data = {
id: 1,
name: 'a1',
children: [{
id: 2,
name: 'a2',
children: [{
id: 3,
name: 'b1',
children: []
},
{
id: 4,
name: 'b2',
children: []
}
]
}]
}
function flatten(data){
output = [];
return (function indentHandler(data, level, parent){
output.push({id: data['id'], parent: parent})
if (data['children'].length === 0){
return output;
}
level += 1;
data['children'].forEach(function(child){
return indentHandler(child, level, data.name);
});
return output;
})(data, 0, null);
}
flatten(data);
use recursion
var tree = {
id: null,
children: [{
id: 1,
children: [{
id: 11,
children: [{
id: 111,
children: []
}]
},
{
id: '12',
children: []
},
]
},
{
id: '2',
children: [{
id: '21',
children: []
},
{
id: '22',
children: [{
id: '221',
children: []
}]
},
]
}
]
};
var flat = [];
function flatArray(arr, parentId) {
arr.forEach(function(el) {
flat.push({
id: el.id,
parent: parentId || null
});
if (el.children)
flatArray(el.children, el.id);
});
}
flatArray(tree.children, 0)
console.log(flat);
I want to filter array of objects by another array of objects.
I have 2 array of objects like this:
const array = [
{ id: 1, name: 'a1', sub: { id: 6, name: 'a1 sub' } },
{ id: 2, name: 'a2', sub: null },
{ id: 3, name: 'a3', sub: { id: 8, name: 'a3 sub' } },
{ id: 4, name: 'a4', sub: null },
{ id: 5, name: 'a5', sub: { id: 10, name: 'a5 sub' } },
];
const anotherArray = [
{ id: 1, name: 'a1', sub: { id: 6, name: 'a1 sub' } },
{ id: 2, name: 'a2', sub: null },
{ id: 5, name: 'a5', sub: { id: 10, name: 'a5 sub' } },
];
and I want filter array by anotherArray and return items that is not exist in anotherArray and have sub.
So my desired output is:
[ { id: 3, name: 'a3', sub: { id: 8, name: 'a3 sub' } ]
Note: I've done this with for loop but it work too slow. I want to do this with using Arrays filter method
Code I have with for loop:
for (let i = 0; i < array.length; i += 1) {
let exist = false;
const item = array[i];
for (let j = 0; j < anotherArray.length; j += 1) {
const anotherItem = anotherArray[j];
if (item.id === anotherItem.id) {
exist = true;
}
}
if (item.sub && !exist) {
this.newArray.push({
text: `${item.sub.name} / ${item.name}`,
value: item.id,
});
}
}
Like Felix mentioned, Array#filter won't work faster than native for loop, however if you really want it as functional way, here's one possible solution:
const array = [
{ id: 1, name: 'a1', sub: { id: 6, name: 'a1 sub' } },
{ id: 2, name: 'a2', sub: null },
{ id: 3, name: 'a3', sub: { id: 8, name: 'a3 sub' } },
{ id: 4, name: 'a4', sub: null },
{ id: 5, name: 'a5', sub: { id: 10, name: 'a5 sub' } },
];
const anotherArray = [
{ id: 1, name: 'a1', sub: { id: 6, name: 'a1 sub' } },
{ id: 2, name: 'a2', sub: null },
{ id: 5, name: 'a5', sub: { id: 10, name: 'a5 sub' } },
];
const r = array.filter((elem) => !anotherArray.find(({ id }) => elem.id === id) && elem.sub);
console.log(r);
You can use Array.filter and then Array.some since the later would return boolean instead of the element like Array.find would:
const a1 = [ { id: 1, name: 'a1', sub: { id: 6, name: 'a1 sub' } }, { id: 2, name: 'a2', sub: null }, { id: 3, name: 'a3', sub: { id: 8, name: 'a3 sub' } }, { id: 4, name: 'a4', sub: null }, { id: 5, name: 'a5', sub: { id: 10, name: 'a5 sub' } }, ];
const a2 = [ { id: 1, name: 'a1', sub: { id: 6, name: 'a1 sub' } }, { id: 2, name: 'a2', sub: null }, { id: 5, name: 'a5', sub: { id: 10, name: 'a5 sub' } }, ];
const result = a1.filter(({id, sub}) => !a2.some(x => x.id == id) && sub)
console.log(result)
You could use JSON.stringify to compare the two objects. It would be better to write a function that compares all properties on the objects recursively.
const array = [
{ id: 1, name: 'a1', sub: { id: 6, name: 'a1 sub' } },
{ id: 2, name: 'a2', sub: null },
{ id: 3, name: 'a3', sub: { id: 8, name: 'a3 sub' } },
{ id: 4, name: 'a4', sub: null },
{ id: 5, name: 'a5', sub: { id: 10, name: 'a5 sub' } },
];
const anotherArray = [
{ id: 1, name: 'a1', sub: { id: 6, name: 'a1 sub' } },
{ id: 2, name: 'a2', sub: null },
{ id: 5, name: 'a5', sub: { id: 10, name: 'a5 sub' } },
];
const notIn = (array1, array2) => array1.filter(item1 => {
const item1Str = JSON.stringify(item1);
return !array2.find(item2 => item1Str === JSON.stringify(item2))
}
);
console.log(notIn(array, anotherArray));
Ok, let's solve this step by step.
To simplify the process let's suppose that two elements can be considered equals if they both have the same id.
The first approach that I would use is to iterate the first array and, for each element, iterate the second one to check the conditions that you've defined above.
const A = [ /* ... */]
const B = [ /* ... */]
A.filter(el => {
let existsInB = !!B.find(e => {
return e.id === el.id
}
return existsInB && !!B.sub
})
If we are sure that the elements in A and in B are really the same when they have the same ID, we could skip all the A elements without the sub property to perform it up a little bit
A.filter(el => {
if (!el.sub) return false
let existsInB = !!B.find(e => {
return e.id === el.id
}
return existsInB
})
Now, if our arrays are bigger than that, it means that we are wasting a lot of time looking for the element into B.
Usually, in these cases, I transform the array where I look for into a map, like this
var BMap = {}
B.forEach(el => {
BMap[el.id] = el
})
A.filter(el => {
if (!el.sub) return false
return !!BMap[el.id]
})
In this way you "waste" a little bit of time to create your map at the beginning, but then you can find your elements quicker.
From here there could be even more optimizations but I think this is enought for this question
OPTIMIZED VERSION
const array = [{
id: 1,
name: "a1",
sub: {
id: 6,
name: "a1 sub"
}
},
{
id: 2,
name: "a2",
sub: null
},
{
id: 3,
name: "a3",
sub: {
id: 8,
name: "a3 sub"
}
},
{
id: 4,
name: "a4",
sub: null
},
{
id: 5,
name: "a5",
sub: {
id: 10,
name: "a5 sub"
}
},
];
const anotherArray = [{
id: 1,
name: "a1",
sub: {
id: 6,
name: "a1 sub"
}
},
{
id: 2,
name: "a2",
sub: null
},
{
id: 5,
name: "a5",
sub: {
id: 10,
name: "a5 sub"
}
},
];
const dict = anotherArray.reduce((acc, curr) => {
const { id } = curr;
acc[id] = curr;
return acc;
}, {});
const result = array.filter((obj) => {
const search = dict[obj.id];
if (!search && obj.sub) return true;
return false;
});
console.log(result);
I want to filter array of objects by another array of objects.
I have 2 array of objects like this:
const array = [
{ id: 1, name: 'a1', sub: { id: 6, name: 'a1 sub' } },
{ id: 2, name: 'a2', sub: null },
{ id: 3, name: 'a3', sub: { id: 8, name: 'a3 sub' } },
{ id: 4, name: 'a4', sub: null },
{ id: 5, name: 'a5', sub: { id: 10, name: 'a5 sub' } },
];
const anotherArray = [
{ id: 1, name: 'a1', sub: { id: 6, name: 'a1 sub' } },
{ id: 2, name: 'a2', sub: null },
{ id: 5, name: 'a5', sub: { id: 10, name: 'a5 sub' } },
];
and I want filter array by anotherArray and return items that is not exist in anotherArray and have sub.
So my desired output is:
[ { id: 3, name: 'a3', sub: { id: 8, name: 'a3 sub' } ]
Note: I've done this with for loop but it work too slow. I want to do this with using Arrays filter method
Code I have with for loop:
for (let i = 0; i < array.length; i += 1) {
let exist = false;
const item = array[i];
for (let j = 0; j < anotherArray.length; j += 1) {
const anotherItem = anotherArray[j];
if (item.id === anotherItem.id) {
exist = true;
}
}
if (item.sub && !exist) {
this.newArray.push({
text: `${item.sub.name} / ${item.name}`,
value: item.id,
});
}
}
Like Felix mentioned, Array#filter won't work faster than native for loop, however if you really want it as functional way, here's one possible solution:
const array = [
{ id: 1, name: 'a1', sub: { id: 6, name: 'a1 sub' } },
{ id: 2, name: 'a2', sub: null },
{ id: 3, name: 'a3', sub: { id: 8, name: 'a3 sub' } },
{ id: 4, name: 'a4', sub: null },
{ id: 5, name: 'a5', sub: { id: 10, name: 'a5 sub' } },
];
const anotherArray = [
{ id: 1, name: 'a1', sub: { id: 6, name: 'a1 sub' } },
{ id: 2, name: 'a2', sub: null },
{ id: 5, name: 'a5', sub: { id: 10, name: 'a5 sub' } },
];
const r = array.filter((elem) => !anotherArray.find(({ id }) => elem.id === id) && elem.sub);
console.log(r);
You can use Array.filter and then Array.some since the later would return boolean instead of the element like Array.find would:
const a1 = [ { id: 1, name: 'a1', sub: { id: 6, name: 'a1 sub' } }, { id: 2, name: 'a2', sub: null }, { id: 3, name: 'a3', sub: { id: 8, name: 'a3 sub' } }, { id: 4, name: 'a4', sub: null }, { id: 5, name: 'a5', sub: { id: 10, name: 'a5 sub' } }, ];
const a2 = [ { id: 1, name: 'a1', sub: { id: 6, name: 'a1 sub' } }, { id: 2, name: 'a2', sub: null }, { id: 5, name: 'a5', sub: { id: 10, name: 'a5 sub' } }, ];
const result = a1.filter(({id, sub}) => !a2.some(x => x.id == id) && sub)
console.log(result)
You could use JSON.stringify to compare the two objects. It would be better to write a function that compares all properties on the objects recursively.
const array = [
{ id: 1, name: 'a1', sub: { id: 6, name: 'a1 sub' } },
{ id: 2, name: 'a2', sub: null },
{ id: 3, name: 'a3', sub: { id: 8, name: 'a3 sub' } },
{ id: 4, name: 'a4', sub: null },
{ id: 5, name: 'a5', sub: { id: 10, name: 'a5 sub' } },
];
const anotherArray = [
{ id: 1, name: 'a1', sub: { id: 6, name: 'a1 sub' } },
{ id: 2, name: 'a2', sub: null },
{ id: 5, name: 'a5', sub: { id: 10, name: 'a5 sub' } },
];
const notIn = (array1, array2) => array1.filter(item1 => {
const item1Str = JSON.stringify(item1);
return !array2.find(item2 => item1Str === JSON.stringify(item2))
}
);
console.log(notIn(array, anotherArray));
Ok, let's solve this step by step.
To simplify the process let's suppose that two elements can be considered equals if they both have the same id.
The first approach that I would use is to iterate the first array and, for each element, iterate the second one to check the conditions that you've defined above.
const A = [ /* ... */]
const B = [ /* ... */]
A.filter(el => {
let existsInB = !!B.find(e => {
return e.id === el.id
}
return existsInB && !!B.sub
})
If we are sure that the elements in A and in B are really the same when they have the same ID, we could skip all the A elements without the sub property to perform it up a little bit
A.filter(el => {
if (!el.sub) return false
let existsInB = !!B.find(e => {
return e.id === el.id
}
return existsInB
})
Now, if our arrays are bigger than that, it means that we are wasting a lot of time looking for the element into B.
Usually, in these cases, I transform the array where I look for into a map, like this
var BMap = {}
B.forEach(el => {
BMap[el.id] = el
})
A.filter(el => {
if (!el.sub) return false
return !!BMap[el.id]
})
In this way you "waste" a little bit of time to create your map at the beginning, but then you can find your elements quicker.
From here there could be even more optimizations but I think this is enought for this question
OPTIMIZED VERSION
const array = [{
id: 1,
name: "a1",
sub: {
id: 6,
name: "a1 sub"
}
},
{
id: 2,
name: "a2",
sub: null
},
{
id: 3,
name: "a3",
sub: {
id: 8,
name: "a3 sub"
}
},
{
id: 4,
name: "a4",
sub: null
},
{
id: 5,
name: "a5",
sub: {
id: 10,
name: "a5 sub"
}
},
];
const anotherArray = [{
id: 1,
name: "a1",
sub: {
id: 6,
name: "a1 sub"
}
},
{
id: 2,
name: "a2",
sub: null
},
{
id: 5,
name: "a5",
sub: {
id: 10,
name: "a5 sub"
}
},
];
const dict = anotherArray.reduce((acc, curr) => {
const { id } = curr;
acc[id] = curr;
return acc;
}, {});
const result = array.filter((obj) => {
const search = dict[obj.id];
if (!search && obj.sub) return true;
return false;
});
console.log(result);