I have a JavaScript object which looks like this:
{
"filters": [
{
"name": "Test",
"items": [
{
"id": 1207700620,
"checked": false
},
{
"id": 1207825584,
"checked": false
},
{
"id": 1207969166,
"checked": true
}
]
},
{
"name": "Empty",
"items": []
},
{
"name": "ThirdList",
"items": [
{
"id": "1207828314",
"checked": true
},
{
"id": "1236723086",
"checked": false
},
{
"id": "1208005603",
"checked": true
}
]
}
]
}
My object have a array by the name filters. This array contains multiple objects. Within each object there is an array by name items. For each element in this items array, I need to check the checked property & if it is false, I need to delete that record (that item in items array).
I need to ensure IE 8 compatability.
I can use a for loop on filters array and within this for loop I can use another for loop to go over items array. I dont want this apporach.
Is there smarter way to achieve this by lodash or jquery or any other JavaScript library??
You can try a combination of forEach and filter methods:
obj.filters.forEach(function(filter) {
filter.items = filter.items.filter(function(el) {
return el.checked;
});
});
Check example below.
var obj = {
"filters": [
{
"name": "Test",
"items": [
{
"id": 1207700620,
"checked": false
},
{
"id": 1207825584,
"checked": false
},
{
"id": 1207969166,
"checked": true
}
]
},
{
"name": "Empty",
"items": []
},
{
"name": "ThirdList",
"items": [
{
"id": "1207828314",
"checked": true
},
{
"id": "1236723086",
"checked": false
},
{
"id": "1208005603",
"checked": true
}
]
}
]
};
obj.filters.forEach(function(filter) {
filter.items = filter.items.filter(function(el) {
return el.checked;
});
});
alert( JSON.stringify(obj, null, 4) );
For IE8 compatibility you can use polyfills or ES5 shims.
Another option if you can use Underscore/Lo-Dash, you can go with analog methods: _.each and _.filter instead of Array.prototype.forEach and Array.prototype.filter:
_.each(obj.filters, function(filter) {
filter.items = _.filter(filter.items, function(el) {
return el.checked;
});
});
Related
Having the following nested array of objects:
[
{
"items": [
{
"name": "See data",
"href": "/data",
},
{
"name": "Account",
"href": "/account",
"icon": {}
}
]
},
{
"items": [
{
"name": "name",
"href": "/name",
"icon": {}
},
{
"name": "My Rooms",
"href": "/rooms",
"icon": {}
}
]
},
{
"items": [
{
"name": "user",
"href": "/user",
"icon": {}
}
]
}
]
How it's possible to remove an inside object by name?
For example to remove the object with name "Account"?
A solution that works is delete myData[0].items[1]; but it's kind of hardcoded.
Also tried like:
myData[0].items = myData[0].items.filter(function (item) {
return item.name !== 'Account';
});
You can use splice to change the original array in-place and findIndex to find the index of the item to remove.
for (const {items} of data) {
const i = items.findIndex(({name}) => name === 'Account');
if (i > -1) items.splice(i, 1);
}
I have an array of objects that looks like this:
[
{
"text":"Same but with checkboxes",
"opened": true,
"children":[
{
"text":"initially selected",
"opened":true
},
]
},
{
"text":"Same but with checkboxes",
"opened":true,
"children":[
{
"text":"initially open",
"opened":true,
"children":[
{
"text":"Another node",
"opened":true,
}
]
},
{
"text":"custom icon",
"opened":true,
},
{
"text":"disabled node",
"opened":true,
}
]
},
{
"text":"And wholerow selection",
"opened":true,
}
]
I want to know if it is possible to change the value for example of the key opened (to false) to all objects at all levels .. how can I do this?
I tried something like that without success
myArray.map(e => ({ ...e, opened: false }))
Make a recursive function - if the object being iterated over has a children array, call it for all such children.
const input=[{text:"Same but with checkboxes",opened:!0,children:[{text:"initially selected",opened:!0}]},{text:"Same but with checkboxes",opened:!0,children:[{text:"initially open",opened:!0,children:[{text:"Another node",opened:!0}]},{text:"custom icon",opened:!0},{text:"disabled node",opened:!0}]},{text:"And wholerow selection",opened:!0}];
const closeAll = (obj) => {
obj.opened = false;
obj.children?.forEach(closeAll);
};
input.forEach(closeAll);
console.log(input);
Recursion is here to help. Recursively search all the objects for opened key and switch it to false.
var data = [ { "text": "Same but with checkboxes", "opened": true, "children": [ { "text": "initially selected", "opened": true }, ] }, { "text": "Same but with checkboxes", "opened": true, "children": [ { "text": "initially open", "opened": true, "children": [ { "text": "Another node", "opened": true, } ] }, { "text": "custom icon", "opened": true, }, { "text": "disabled node", "opened": true, } ] }, { "text": "And wholerow selection", "opened": true, } ];
function run(data) {
for (let subData of data) {
if (subData["opened"])
subData["opened"] = false;
if (subData["children"])
run(subData["children"])
}
}
run(data)
console.log(data)
Just extend your map method to handle recursively. (children exist or not and Array or single object)
const updateOpened = (data) => {
if (Array.isArray(data)) {
return data.map(updateOpened);
}
const { children, ...item } = data;
return children
? { ...updateOpened(item), children: updateOpened(children) }
: { ...item, opened: true };
};
const arr=[{text:"Same but with checkboxes",opened:!0,children:[{text:"initially selected",opened:!0}]},{text:"Same but with checkboxes",opened:!0,children:[{text:"initially open",opened:!0,children:[{text:"Another node",opened:!0}]},{text:"custom icon",opened:!0},{text:"disabled node",opened:!0}]},{text:"And wholerow selection",opened:!0}];
console.log(updateOpened(arr));
This question already has answers here:
How to deep merge instead of shallow merge?
(47 answers)
Closed 3 years ago.
I have a couple of Objects with the same properties. I want to combine all of the object which has the same first level key-value. I am aware of the spread operator
const obj3 = {...obj1, ...obj2}
but the problem is, the arrays inside the object are getting overwritten and not merged.
{
"id": 1,
"name": "firstLevel",
"visible": true,
"subCategories": [
{
"id": 2,
"name": "secondLevel",
"visible": true,
"skills": [
{
"name": "foo",
"id": 5,
"visible": true
}
]
}
]
}
{
"id": 1,
"name": "firstLevel",
"visible": true,
"subCategories": [
{
"id": 2,
"name": "secondLevel",
"visible": true,
"skills": [
{
"name": "bar",
"id": 1,
"visible": true
}
]
}
]
}
I expect the objects to combine like that :
{
"id": 1,
"name": "firstLevel",
"visible": true,
"subCategories": [
{
"id": 2,
"name": "secondLevel",
"visible": true,
"skills": [
{
"name": "foo",
"id": 5,
"visible": true
},
{
"name": "bar",
"id": 1,
"visible": true
}
]
}
]
}
You are looking for lodash.mergeWith, which merges arrays and plain object properties recursively.
var object = {
'a': [{ 'b': 2 }, { 'd': 4 }]
};
var other = {
'a': [{ 'c': 3 }, { 'e': 5 }]
};
function customizer(objValue, srcValue) {
if (_.isArray(objValue)) {
return objValue.concat(srcValue);
}
}
_.mergeWith(object, other, customizer);
// => { 'a': [{ 'b': 2 }, { 'c': 3 }, { 'd': 4 }, { 'e': 5 }] }
For your particular case when you know the ids of the objects, this customizer function will work as requested.
function customizer(objValue, srcValue) {
if (_.isArray(objValue)) {
for (const srcItem of srcValue) {
const objItem = objValue.filter(item => item.id === srcItem.id);
if (objItem.length) {
objValue = objValue.map(item => {
if (item.id === objItem[0].id) {
return _.mergeWith(item, srcItem, customizer);
}
return item;
});
} else {
objValue = [...objValue, srcItem];
}
}
return objValue;
}
}
How to create a lodash to get the userID 10165381978 and its children userIDs for the following multi array ($scope.myVar)-
{
"children": [{
"userId": "1",
"name": "Kevin",
"type": "ROOT",
"active": true,
"children": [{
"userId": "10023872531",
"name": "Selvin",
"type": "USER",
"active": true,
"children": []
}, {
"userId": "10003200835",
"name": "adduser",
"type": "USER",
"active": true,
"children": []
}, {
"userId": "-1111111",
"name": "Merisa",
"type": "USER",
"active": true,
"children": []
}, {
"userId": "10165381976",
"name": "Kam",
"type": "PARENT",
"active": true,
"children": [{
"userId": "10165381977",
"name": "Pam",
"type": "USER",
"active": true,
"children": [{
"userId": "10165381978",
"name": "Ram",
"type": "PARENT",
"active": true,
"children": [{
"userId": "10232392492",
"name": "Sam",
"type": "USER",
"active": true,
"children": []
}]
}]
}]
}]
}]
}
So from above I want to get-
10165381978, 10232392492
I tried-
var filterUser = function(userId){
lodash.filter($scope.myVar, function(o) { return o.userId})
}
And then called filterUser(10165381978);
But the result was empty object [].
Lodash does not provide a built-in utility to achieve this. You can solve this by recursively traversing throughout your nested collections. Check the solution below, each section of the code is commented.
function getIds(collection, ids) {
// store ids in this variable
var result = [];
// determines if an id is found,
var found = false;
// makes sure that ids is always an array of id
ids = [].concat(ids);
// iterate over the collection, name the callback for recursion
// if you prefer to use lodash, then use:
// _.each(collection, function iterator(value) {...});
collection.forEach(function iterator(value) {
// Matching the list of `ids` from the iterated userId.
// If a match is found, then we set `found` to true.
var isStop = ~ids.indexOf(value.userId) && (found = true);
// did we get a match?
if(found) {
// add the matched ID and the IDs from its descendants
result.push(value.userId);
}
// itereate recursively over its descendants
// If you prefer to use lodash then use:
// _.each(value.children, iterator)
(value.children || []).forEach(iterator);
// is the currently iterated item's ID within the list of `ids`?
if(isStop) {
// set `found` to false, to prevent adding IDs that aren't matched
found = false;
}
});
// return an array of IDs
return result;
}
var data = {
"children": [{
"userId": "1",
"name": "Kevin",
"type": "ROOT",
"active": true,
"children": [{
"userId": "10023872531",
"name": "Selvin",
"type": "USER",
"active": true,
"children": []
}, {
"userId": "10003200835",
"name": "adduser",
"type": "USER",
"active": true,
"children": []
}, {
"userId": "-1111111",
"name": "Merisa",
"type": "USER",
"active": true,
"children": []
}, {
"userId": "10165381976",
"name": "Kam",
"type": "PARENT",
"active": true,
"children": [{
"userId": "10165381977",
"name": "Pam",
"type": "USER",
"active": true,
"children": [{
"userId": "10165381978",
"name": "Ram",
"type": "PARENT",
"active": true,
"children": [{
"userId": "10232392492",
"name": "Sam",
"type": "USER",
"active": true,
"children": []
}]
}]
}]
}]
}]
};
function getIds(collection, ids) {
// store ids in this variable
var result = [];
// determines if an id is found,
var found = false;
// makes sure that ids is always an array of id
ids = [].concat(ids);
// iterate over the collection, name the callback for recursion
// if you prefer to use lodash, then use:
// _.each(collection, function iterator(value) {...});
collection.forEach(function iterator(value) {
// Matching the list of `ids` from the iterated userId.
// If a match is found, then we set `found` to true.
var isStop = ~ids.indexOf(value.userId) && (found = true);
// did we get a match?
if(found) {
// add the matched ID and the IDs from its descendants
result.push(value.userId);
}
// itereate recursively over its descendants
// If you prefer to use lodash then use:
// _.each(value.children, iterator)
(value.children || []).forEach(iterator);
// is the currently iterated item's ID within the list of `ids`?
if(isStop) {
// set `found` to false, to prevent adding IDs that aren't matched
found = false;
}
});
// return an array of IDs
return result;
}
console.log('ID to find: 10165381978');
console.log(getIds(data.children, '10165381978'));
console.log('IDs to find: 10023872531, 10165381976');
console.log(getIds(data.children, [
'10023872531',
'10165381976'
]));
.as-console-wrapper {
min-height: 100%;
top: 0;
}
I need to filter a subarray of elements.
var university = {
"fax": "123345",
"email": "test#test.com",
"url": "www.test.com",
"classes": [
{
"number": "1",
"name": "maths",
"students": [
{
"name": "Max",
"exams": [
{
"date": "2016-01-04T18:32:43.000Z",
"passed": false
},
{
"date": "2016-01-04T18:32:43.000Z",
"passed": true
},
{
"date": "2016-01-04T18:32:43.000Z",
"passed": false
},
{
"date": "2016-01-04T18:32:43.000Z",
"passed": true
}
]
},
{...}
]
},
{...}
]
}
Ok I need to get all the classes without filtering, all the students of each class without filtering, but in the exams array I only need to get the ones that passed.
I tried the following:
university.classes.students.exams.filter(function (el) {
return el.passed
});
But it is not working...
I've googled a solution to this without success...any help would be appreciated.
classes and students are arrays - so you have to loop those as well:
university.classes.forEach(function(uniClass) {
uniClass.students.forEach(function(student) {
student.exams = student.exams.filter(function (el) {
return el.passed;
});
});
});