i have an parent array of objects
const parent = [{name: 'one', id: 1}, {name: 'two', id: 2}, {name: 'three', id: 3}, {name: 'four', id: 4}, {name: 'five', id: 5}]
i have made sub arrays out of it
const childrenCollection = [[{name: 'one', id: 1}, {name: 'two', id: 2}], [{name: 'three', id: 3}, {name: 'four', id: 4}], [{name: 'five', id: 5}]]
now i am looping through the childrenCollection
childrenCollection.map(childrenSubCollection => {
// find the actual index from the main parent array
const indexOfOne = childrenSubCollection.findIndex(childrenSub => {
return childrenSub[0].id === // ??
})
const indexOfTwo = childrenSubCollection.findIndex(childrenSub => {
return childrenSub[1].id === // ??
})
console.log(childrenSubCollection[0], childrenSubCollection[1], 'index', indexOfOne, indexOfTwo )
})
How can i find the index of each from parent array
Using Array#findIndex:
const
parent = [ { name: 'one', id: 1 }, { name: 'two', id: 2 }, { name: 'three', id: 3 }, { name: 'four', id: 4 }, { name: 'five', id: 5 } ],
childrenCollection = [
[ { name: 'one', id: 1 }, { name: 'two', id: 2 } ],
[ { name: 'three', id: 3 }, { name: 'four', id: 4 } ],
[ { name: 'five', id: 5 } ]
];
childrenCollection.forEach(([ one, two ]) => {
const indexOfOne = one ? parent.findIndex(item => one.id === item.id) : -1;
const indexOfTwo = two ? parent.findIndex(item => two.id === item.id) : -1;
console.log(one, two, 'index', indexOfOne, indexOfTwo);
});
Using Map:
const
parent = [ { name: 'one', id: 1 }, { name: 'two', id: 2 }, { name: 'three', id: 3 }, { name: 'four', id: 4 }, { name: 'five', id: 5 } ],
childrenCollection = [
[ { name: 'one', id: 1 }, { name: 'two', id: 2 } ],
[ { name: 'three', id: 3 }, { name: 'four', id: 4 } ],
[ { name: 'five', id: 5 } ]
];
const indexMap = new Map( parent.map(({ id }, index) => ([ id, index ])) );
childrenCollection.forEach(([ one, two ]) => {
const indexOfOne = one ? indexMap.get(one.id) : -1;
const indexOfTwo = two ? indexMap.get(two.id) : -1;
console.log(one, two, 'index', indexOfOne, indexOfTwo);
});
Related
I have an array with objects. I need to find item with current name and change it.
const example = [
{
id: '1234',
desc: 'sample1',
items: [
itemsName: [
{ id: 1, name: 'name1' },
{ id: 2, name: 'testItem2' }
],
id: 888,
]
},
{
id: '3456',
desc: 'sample2',
items: [
itemsName: [
{ id: 1, name: 'name2' },
{ id: 2, name: 'testItem3' }
],
id: 889,
]
},
I try to do in this way, but it's not working.
I get construction like (5)
[Array(1), Array(1)]
instead of
[{…}, {…}]
const findName = (name, changedName) => {
const result = example?.map((group) =>
group.items.map((group) =>
group.itemsName?.map((i) => {
if (i.name === name) return i.name === changedName;
return null;
})
)
);
}
findName('name1', 'name2')
let findName1 = (name, changedName) => {
const result = example?.map((group) =>
group.items.map((group) =>
group.itemsName?.map((i) => {
if (i.name === name) return i.name = changedName;
return null;
})
)
);
}
This will work with following object (your object declaration seems to be wrong)
const example = [
{
id: '1234',
desc: 'sample1',
items: [
{itemsName: [
{ id: 1, name: 'name1' },
{ id: 2, name: 'testItem2' }
],
id: 888,}
]
},
{
id: '3456',
desc: 'sample2',
items: [
{itemsName: [
{ id: 1, name: 'name2' },
{ id: 2, name: 'testItem3' }
],
id: 889,}
]
}]
I ran into a problem that I don’t know how to correctly compose a recursive function. the logic is such that if you delete an item in the application, then all items whose parentId is equal to the id of the item that decided to delete are deleted, and so on. I hope I explained well, I will be very grateful for the help!
const arr = [
{title: 'one', id: 1, parentId: null},
{title: 'two', id: 2, parentId: 1},
{title: 'three', id: 3, parentId: null},
{title: 'four', id: 4, parentId: 2},
{title: 'five', id: 5, parentId: 2},
{title: 'six', id: 6, parentId: 5},
{title: 'seven', id: 7, parentId: 3},
]
// if we decide to remove "one", the result will be
[
{title: 'three', id: 3, parentId: null},
{title: 'seven', id: 7, parentId: 3},
]
I'm stuck on a version that removes subtasks with only one parentId level
const deleteFunc = (array, value) => {
array = array.filter(item => item.id !== value);
const findSubTask = array.filter((item) => item.parentId === value);
array = array.filter(item => item.id !== findSubTask[0].id);
const findSecondSubTask = array.find(
(key) => key.parentId === findSubTask[0].id,
);
if (findSecondSubTask) {
return deleteFunc(array, findSubTask[0].id);
}
return array;
};
console.log(deleteFunc(arr, 1));
Get the IDs of all the descendants of the starting element using a recursive function. Then remove them.
const arr = [
{title: 'one', id: 1, parentId: null},
{title: 'two', id: 2, parentId: 1},
{title: 'three', id: 3, parentId: null},
{title: 'four', id: 4, parentId: 2},
{title: 'five', id: 5, parentId: 2},
{title: 'six', id: 6, parentId: 5},
{title: 'seven', id: 7, parentId: 3},
];
function getDescendantIds(id, arr) {
let result = [id];
arr.forEach(el => {
if (el.parentId == id) {
result = result.concat(getDescendantIds(el.id, arr));
}
});
return result;
}
function removeTitleAndDescendants(title, arr) {
let start = arr.find(el => el.title == title);
if (start) {
let allIds = new Set(getDescendantIds(start.id, arr));
return arr.filter(el => !allIds.has(el.id));
}
return [...arr];
}
let result = removeTitleAndDescendants("one", arr);
console.log(result);
console.config({
maxEntries: Infinity
});
There are definitely a number of approaches to this question.
You could go ahead and keep a dictionary that will map parent IDs -> Items, so that you could easily perform what you're triyng to do here in a an efficient manner.
Despite that, if we are looking specifically for a javascript solution, I'd try something along the lines of:
const arr = [
{title: 'one', id: 1, parentId: null},
{title: 'two', id: 2, parentId: 1},
{title: 'three', id: 3, parentId: null},
{title: 'four', id: 4, parentId: 2},
{title: 'five', id: 5, parentId: 2},
{title: 'six', id: 6, parentId: 5},
{title: 'seven', id: 7, parentId: 3},
]
function deleteFromList(titleToDelete) {
deletedList = [];
itemToDeleteIdx = arr.indexOf(i=>i.title === titleToDelete);
if(itemToDeleteIdx !== -1){
deletedList.push(arr[itemToDeleteIdx]);
arr.splice(itemToDeleteIdx, 1);
return deleteRecursive(deleteRecursive[0].parentId, deletedList);
} else {
return [];
}
}
function deleteRecursive(parentIdToDelete, deletedItemsList){
itemToDeleteIdx = arr.indexOf(i=>i.parentId === parentIdToDelete);
if(itemToDeleteIdx !== -1){
deletedItemsList.push(arr[itemToDeleteIdx]);
arr.splice(itemToDeleteIdx, 1);
return deleteRecursive(parentIdToDelete, deletedItemsList)
}
else {
return deletedItemsList;
}
}
console.log(deleteFromList('three'))
Obviously it's missing a lot of validations and corner cases, but that's the main flow of things.
The function you see below takes an array of item ids you want to delete so at the beginning you give an array with the id of the item you want to delete.
then it does two simple tasks: delete items in the passed array, find the ids of all items which has the previous item as their parentId and add them to an array you pass to the next call and so on.
function deleteItems(arrIds) {
if(arrIds.length === 0) return;
arr = arr.filter((item) => !arrIds.includes(item.id));
const newIds = [];
arr.forEach((item) => {
if(arrIds.includes(item.parentId)) newIds.push(item.id);
})
deleteItems(newIds);
};
deleteItems([1]);
Let's convert to a tree:
const arr = [
{title: 'one', id: 1, parentId: null},
{title: 'two', id: 2, parentId: 1},
{title: 'three', id: 3, parentId: null},
{title: 'four', id: 4, parentId: 2},
{title: 'five', id: 5, parentId: 2},
{title: 'six', id: 6, parentId: 5},
{title: 'seven', id: 7, parentId: 3},
];
var grouped = arr.reduce(function(agg, item) {
agg[item.id] = item
return agg;
}, {})
arr.forEach(function(item) {
if (item.parentId) {
grouped[item.parentId].children ??= []
grouped[item.parentId].children.push(item.id)
}
})
// this is our tree
console.log(grouped)
// now we can:
function get_children_deep(tree, id) {
var result = [];
function iterate(tree, id) {
var obj = tree[id];
(obj.children || []).forEach(function(child_id) {
result.push(child_id);
iterate(tree, child_id);
})
}
iterate(tree, id)
return result;
}
console.log("all descendants of 1: " + get_children_deep(grouped, 1))
.as-console-wrapper {max-height: 100% !important}
So based on that, here's the solution of deleting item and children by title. recursively.
const arr = [
{title: 'one', id: 1, parentId: null},
{title: 'two', id: 2, parentId: 1},
{title: 'three', id: 3, parentId: null},
{title: 'four', id: 4, parentId: 2},
{title: 'five', id: 5, parentId: 2},
{title: 'six', id: 6, parentId: 5},
{title: 'seven', id: 7, parentId: 3},
];
console.log(delete_by_title(arr, "one"));
function delete_by_title(arr, title) {
var grouped = arr.reduce(function(agg, item) {
agg[item.id] = item
return agg;
}, {})
arr.forEach(function(item) {
if (item.parentId) {
grouped[item.parentId].children ??= []
grouped[item.parentId].children.push(item.id)
}
})
function get_children_deep(tree, id) {
var result = [];
function iterate(tree, id) {
var obj = tree[id];
(obj.children || []).forEach(function(child_id) {
result.push(child_id);
iterate(tree, child_id);
})
}
iterate(tree, id)
return result;
}
function id_by_title(arr, title) {
return arr.find(function(item) {
return item.title == title
}).id;
}
var id = id_by_title(arr, title)
var to_delete = get_children_deep(grouped, id)
to_delete.push(id)
to_delete.forEach(function(id) {
delete grouped[id]
})
return Object.values(grouped);
}
if you want to return them then the below should do the trick
const arr = [
{title: 'one', id: 1, parentId: null},
{title: 'two', id: 2, parentId: 1},
{title: 'three', id: 3, parentId: null},
{title: 'four', id: 4, parentId: 2},
{title: 'five', id: 5, parentId: 2},
{title: 'six', id: 6, parentId: 5},
{title: 'seven', id: 7, parentId: 3},
]
const deleteId = (val) => {
const resultArray = [];
const index = arr.findIndex((n) => n.id === val);
for(let i =0; i < arr.length; i++)
{
if(arr[i].id === val || arr[i].parentId === arr[index].id)
{
resultArray.push(arr[i]);
}
}
return resultArray;
}
const result = deleteId(1);
console.log(result);
Essentially the same idea as #Barmar, but done in a different way :)
const arr = [
{title: 'one', id: 1, parentId: null},
{title: 'two', id: 2, parentId: 1},
{title: 'three', id: 3, parentId: null},
{title: 'four', id: 4, parentId: 2},
{title: 'five', id: 5, parentId: 2},
{title: 'six', id: 6, parentId: 5},
{title: 'seven', id: 7, parentId: 3},
];
function cascadingDelete(arr, title) {
const index = arr.findIndex((e) => e.title === title);
const indices = [index].concat((function walk(parentId) {
const children = arr.filter((e) => e.parentId === parentId);
const indices = children.map((c) => arr.indexOf(c));
return indices.concat(children.map((c) => walk(c.id)));
})(arr[index].id)).flat(Infinity);
return arr.filter((_, i) => !indices.includes(i));
}
console.log(cascadingDelete(arr, "one"));
I am struggling to find out a solution to my problem, but at the moment I cannot come up with the right one.
When I have my two arrays of objects I want to filter based on category IDs and extract the data from the second one into a new array for example :
const array1 = [
{id: 1, name: 'Tropical'},
{id: 2, name: 'Common'}
]
const array2 = [
{id:1, name: 'Banana', category_id: 1},
{id:2, name: 'Mango', category_id: 1},
{id:3, name: 'Apple', category_id: 2},
]
And when click happens I detect the first ID and render the new array only with data that matches the ID.
Click Tropical
New array :
[
{id:1, name: 'Banana', category_id: 1},
{id:2, name: 'Mango', category_id: 1},
]
I would be happy if someone give me a hint on how can I tackle this problem. Thanks !
Correct me if I am wrong, So you need a function that received a categoryId and you need to filter out array2 based on that category_id
You can try this
const array1 = [{
id: 1,
name: 'Tropical'
},
{
id: 2,
name: 'Common'
}
]
const array2 = [{
id: 1,
name: 'Banana',
category_id: 1
},
{
id: 2,
name: 'Mango',
category_id: 1
},
{
id: 3,
name: 'Apple',
category_id: 2
},
]
function categoryFruits(categoryId) {
return array2.filter(obj => obj.id === categoryId)
}
console.log(categoryFruits(3));
Use reduce to map over each item in array1 and filter to grab the items of that category_id
const array1 = [{
id: 1,
name: 'Tropical'
},
{
id: 2,
name: 'Common'
}
]
const array2 = [{
id: 1,
name: 'Banana',
category_id: 1
},
{
id: 2,
name: 'Mango',
category_id: 1
},
{
id: 3,
name: 'Apple',
category_id: 2
},
]
const obj = array1.reduce((acc, cur) => {
acc[cur.name] = array2.filter(v => v.category_id === cur.id)
return acc
}, {})
console.log(obj)
You could do something like filtering array2 and taking all the elements that have Tropical as name in array1.
const array1 = [
{id: 1, name: 'Tropical'},
{id: 2, name: 'Common'}
]
const array2 = [
{id:1, name: 'Banana', category_id: 1},
{id:2, name: 'Mango', category_id: 1},
{id:3, name: 'Apple', category_id: 2},
]
// take tropical friuts
let tropicalFriuts = array2.filter(x => x.category_id === array1.filter(y => y.name === 'Tropical')[0].id);
console.log(tropicalFriuts);
If I understood your problem you want before find the id, based on the name of the category, and later filter array2 data based on this id.
const array1 = [{
id: 1,
name: 'Tropical'
},
{
id: 2,
name: 'Common'
}
]
const array2 = [{
id: 1,
name: 'Banana',
category_id: 1
},
{
id: 2,
name: 'Mango',
category_id: 1
},
{
id: 3,
name: 'Apple',
category_id: 2
},
]
const id_key = array1.find(item=>item.name === 'Tropical').id;
const result = array2.filter(item=>item.category_id === id_key);
console.log(result);
How could I solve a case where I have to merge trees with repeated values? For example something like that:
const firstTree = {
id: 1,
name: 'name1',
otherProperty: 'property1',
children: [
{
id: '1a',
name: 'name1a',
otherProperty: 'property1a',
children: []
},
{
id: '1b',
name: 'name1b',
otherProperty: 'property1b',
children: [
{
id: '1ba',
name: 'name1ba',
otherProperty: 'property1ba',
children: []
},
{
id: '1bb',
name: 'name1bb',
otherProperty: 'property1bb',
children: []
}
]
}
]
};
const secondTree = {
id: 1,
name: 'name1',
otherProperty: 'property1',
children: [
{
id: '1b',
name: 'name1b',
otherProperty: 'property1b',
children: [
{
id: '1ba',
name: 'name1ba',
otherProperty: 'property1ba',
children: []
},
{
id: '2ba',
name: 'name2ba',
otherProperty: 'property2ba',
children: []
}
]
}
]
};
const thirdTree = {
id: '3',
name: 'name3',
otherProperty: 'property3',
children: [
{
id: '3a',
name: 'name3a',
otherProperty: 'property3a',
children: []
},
{
id: '3b',
name: 'name3b',
otherProperty: 'property3b',
children: []
}
]
};
const entryArray = [firstTree, secondTree, thirdTree];
And I want to have as a result merged the first tree with the second and additionaly the third tree where there were no common elements :
const mergedFirstAndSecond = {
id: 1,
name: 'name1',
otherProperty: 'property1',
children: [
{
id: '1a',
name: 'name1a',
otherProperty: 'property1a',
children: []
},
{
id: '1b',
name: 'name1b',
otherProperty: 'property1b',
children: [
{
id: '1ba',
name: 'name1ba',
otherProperty: 'property1ba',
children: []
},
{
id: '1bb',
name: 'name1bb',
otherProperty: 'property1bb',
children: []
},
{
id: '2ba',
name: 'name2ba',
otherProperty: 'property2ba',
children: []
}
]
}
]
};
const result = [mergedFirstAndSecond, thirdTree];
I mean a solution that would also work if the duplicate elements also occurred in three different trees, not just two. I will be very grateful for any suggestions.
You could first create the tree as a nested map structure (where each children property is a Map instance), and merge all trees in that structure. This will allow optimised lookup to see where an entry needs to be merged into.
Once you have that, replace all these children properties back to arrays.
function mergeTrees(...trees) {
function fillMap(src, map) {
let dst = map.get(src.id);
if (!dst) map.set(src.id, dst = { ...src, children: new Map });
for (let child of (src.children ?? [])) fillMap(child, dst.children);
}
// Merge into nested Map structure:
let mergedTree = new Map;
for (let tree of trees) fillMap(tree, mergedTree);
// Convert each map to array:
const toArrays = map => Array.from(map.values(), node =>
Object.assign(node, { children: toArrays(node.children) })
);
return toArrays(mergedTree);
}
// Demo
const firstTree = {id: 1,name: 'name1',otherProperty: 'property1',children: [{id: '1a',name: 'name1a',otherProperty: 'property1a',children: []}, {id: '1b',name: 'name1b',otherProperty: 'property1b',children: [{id: '1ba',name: 'name1ba',otherProperty: 'property1ba',children: []}, {id: '1bb',name: 'name1bb',otherProperty: 'property1bb',children: []}]}]};
const secondTree = {id: 1,name: 'name1',otherProperty: 'property1',children: [{id: '1b',name: 'name1b',otherProperty: 'property1b',children: [{id: '1ba',name: 'name1ba',otherProperty: 'property1ba',children: []}, {id: '2ba',name: 'name2ba',otherProperty: 'property2ba',children: []}]}]};
const thirdTree = {id: '3',name: 'name3',otherProperty: 'property3',children: [{id: '3a',name: 'name3a',otherProperty: 'property3a',children: []}, {id: '3b',name: 'name3b',otherProperty: 'property3b',children: []}]};
const entryArray = mergeTrees(firstTree, secondTree, thirdTree);
console.log(entryArray);
You can use recursion to merge the trees on their ids:
var firstTree = {'id': 1, 'name': 'name1', 'otherProperty': 'property1', 'children': [{'id': '1a', 'name': 'name1a', 'otherProperty': 'property1a', 'children': []}, {'id': '1b', 'name': 'name1b', 'otherProperty': 'property1b', 'children': [{'id': '1ba', 'name': 'name1ba', 'otherProperty': 'property1ba', 'children': []}, {'id': '1bb', 'name': 'name1bb', 'otherProperty': 'property1bb', 'children': []}]}]};
var secondTree = {'id': 1, 'name': 'name1', 'otherProperty': 'property1', 'children': [{'id': '1b', 'name': 'name1b', 'otherProperty': 'property1b', 'children': [{'id': '1ba', 'name': 'name1ba', 'otherProperty': 'property1ba', 'children': []}, {'id': '2ba', 'name': 'name2ba', 'otherProperty': 'property2ba', 'children': []}]}]};
var thirdTree = {'id': '3', 'name': 'name3', 'otherProperty': 'property3', 'children': [{'id': '3a', 'name': 'name3a', 'otherProperty': 'property3a', 'children': []}, {'id': '3b', 'name': 'name3b', 'otherProperty': 'property3b', 'children': []}]};
var entryArray = [firstTree, secondTree, thirdTree];
function merge_trees(trees){
var merger = {};
//find matches based on id
for (var t of trees){
for (var i of t){
if (!(i['id'] in merger)){
merger[i['id']] = Object.fromEntries(Object.keys(i).map(x => [x, (new Set([i[x]]))]));
}
for (var k of Object.keys(i)){
merger[i['id']][k] = new Set([...(k in merger[i['id']] ? merger[i['id']][k] : []), i[k]]);
}
}
}
var new_result = [];
//iterate over merges
for (var i of Object.keys(merger)){
var result = {}
for (var k of Object.keys(merger[i])){
//choose whether or not to merge again based on the size of the merged children
if (k === 'children'){
result[k] = merger[i][k].size > 1 ? merge_trees(merger[i][k]) : [...merger[i][k]].filter(x => x.length > 1)
}
else{
result[k] = merger[i][k].size === 1 ? [...merger[i][k]][0] : [...merger[i][k]]
}
}
new_result.push(result)
}
return new_result;
}
console.log(merge_trees(entryArray.map(x => [x])))
Need to merge 2 lists of updates, local and server.
Using redux (but it doesn't really matter) i need to refresh the updates list.
const localUpdates = [
{ id: 1, name: 'one', date: 'old' },
{ id: 2, name: 'two', date: 'old' },
{ id: 3, name: 'three', date: 'old' },
];
const serverUpdates = [
{ id: 1, name: 'one', date: 'new' },
{ id: 4, name: 'four', date: 'new' },
];
Desired output:
updates: [
{ id: 1, name: 'one', date: 'new' },
{ id: 2, name: 'two', date: 'old' },
{ id: 3, name: 'three', date: 'old' },
{ id: 4, name: 'four', date: 'new' },
]
Thanks in advance (having brainfreeze looiking at this for the past hour)
You can make use of Array.prototype.reduce followed by Object.values to the concated array in order to perform an update
const localUpdates = [
{ id: 1, name: 'one', date: 'old' },
{ id: 2, name: 'two', date: 'old' },
{ id: 3, name: 'three', date: 'old' },
];
const serverUpdates = [
{ id: 1, name: 'one', date: 'new' },
{ id: 4, name: 'four', date: 'new' },
];
const concatArr = localUpdates.concat(serverUpdates);
// we will be excepting new values for same id
const resObj = concatArr.reduce((acc, item)=> {
acc[item.id] = {...(acc[item.id] || {}), ...item};
return acc;
}, {})
console.log(Object.values(resObj));
create a temporary object, loop over both arrays and add each object's id in the temporay object as key and whole object as value. Objects with same id's will be overwritten. Finally use Object.values to get the merged objects in an array.
const localUpdates = [
{ id: 1, name: 'one', date: 'old' },
{ id: 2, name: 'two', date: 'old' },
{ id: 3, name: 'three', date: 'old' },
];
const serverUpdates = [
{ id: 1, name: 'one', date: 'new' },
{ id: 4, name: 'four', date: 'new' },
];
const obj = {};
localUpdates.forEach(local => (obj[local.id] = local));
serverUpdates.forEach(server => (obj[server.id] = server));
console.log(Object.values(obj));
.as-console-wrapper { max-height: 100% !important; top: 0; }