Lodash - find value in nested Objects - javascript

I have an object structure like as mentioned below in link
https://jsonblob.com/3511b867-dd4b-11e9-85e4-63b804994c26
I want to search say firstname: Dianca in all the nodes under persons using lodash.
I do not want to harcode the node names as that way i have mention a lot of nodes to look for. I need to search it dynamically under person node no matter where it is located under Object structure.
I have tried a way like as below but in vain(like harcoded stuff)
_.filter(users, o =>
_.some(o.Positions, ['persons.firstname', 'Dianca'])
)
and secondly, i want to get all names under persons node and save it to new array..
Any assistance would be appreciated.

What a Question !! It took me an year to build a logic for you :)
function findUser(data, firstName) {
var object;
data.some(function f(a) {
if (a.firstname == firstName) {
object = a;
return true;
}
_.each(Object.keys(a), function (value, key) {
// loops through json objects in waypath
if (!_.isEmpty(object)) {
return false; // stop once we get the user
}
if (isNaN(value)) {
// only data with string keys need to iterate more
// when there is an array with elements
if (Array.isArray(a[value]) && a[value].length != 0) {
a[value].some(f); // recursion (Didn't use return because we are in loop)
} else if (!_.isEmpty(a[value])) {
// when there is an object within object
_.each(Object.keys(a[value]), function (val, key2) {
if (Array.isArray(a[value][val]) && a[value][val].length != 0) {
a[value][val].some(f); // recursion
}
});
}
}
});
});
return object;
}
console.log(findUser(data, "Phyllis"));
Should be exactly what you need. Enjoy !!

Related

How to check if an object's keys and deep keys are equal, similar to lodash's isEqual?

So I'm in a unique situation where I have two objects, and I need to compare the keys on said objects to make sure they match the default object. Here's an example of what I'm trying to do:
const _ = require('lodash');
class DefaultObject {
constructor(id) {
this.id = id;
this.myobj1 = {
setting1: true,
setting2: false,
setting3: 'mydynamicstring'
};
this.myobj2 = {
perm1: 'ALL',
perm2: 'LIMITED',
perm3: 'LIMITED',
perm4: 'ADMIN'
};
}
}
async verifyDataIntegrity(id, data) {
const defaultData = _.merge(new DefaultObject(id));
if (defaultData.hasOwnProperty('myoldsetting')) delete defaultData.myoldsetting;
if (!_.isEqual(data, defaultData)) {
await myMongoDBCollection.replaceOne({ id }, defaultData);
return defaultData;
} else {
return data;
}
}
async requestData(id) {
const data = await myMongoDBCollection.findOne({ id });
if (!data) data = await this.makeNewData(id);
else data = await this.verifyDataIntegrity(id, data);
return data;
}
Let me explain. First, I have a default object which is created every time a user first uses the service. Then, that object is modified to their customized settings. For example, they could change 'setting1' to be false while changing 'perm2' to be 'ALL'.
Now, an older version of my default object used to have a property called 'myoldsetting'. I don't want newer products to have this setting, so every time a user requests their data I check if their object has the setting 'myoldsetting', and if it does, delete it. Then, to prevent needless updates (because this is called every time a user wants their data), I check if it is equal with the new default object.
But this doesn't work, because if the user has changed a setting, it will always return false and force a database update, even though none of the keys have changed. To fix this, I need a method of comparing the keys on an object, rather any the keys and data.
That way, if I add a new option to DefaultObject, say, 'perm5' set to 'ADMIN', then it will update the user's object. But, if their object has the same keys (it's up to date), then continue along your day.
I need this comparison to be deep, just in case I add a new property in, for example, myobj1. If I only compare the main level keys (id, myobj1, myobj2), it won't know if I added a new key into myobj1 or myobj2.
I apologize if this doesn't make sense, it's a very specific situation. Thanks in advance if you're able to help.
~~~~EDIT~~~~
Alright, so I've actually come up with a function that does exactly what I need. The issue is, I'd like to minify it so that it's not so big. Also, I can't seem to find a way to check if an item is a object even when it's null. This answer wasn't very helpful.
Here's my working function.
function getKeysDeep(arr, obj) {
Object.keys(obj).forEach(key => {
if (typeof obj[key] === 'object') {
arr = getKeysDeep(arr, obj[key]);
}
});
arr = arr.concat(Object.keys(obj));
return arr;
}
Usage
getKeysDeep([], myobj);
Is it possible to use it without having to put an empty array in too?
So, if I understand you correctly you would like to compare the keys of two objects, correct?
If that is the case you could try something like this:
function hasSameKeys(a, b) {
const aKeys = Object.keys(a);
const bKeys = Object.keys(b);
return aKeys.length === bKeys.length && !(aKeys.some(key => bKeys.indexOf(key) < 0));
}
Object.keys(x) will give you all the keys of the objects own properties.
indexOf will return a -1 if the value is not in the array that indexOf is being called on.
some will return as soon as the any element of the array (aKeys) evaluates to true in the callback. In this case: If any of the keys is not included in the other array (indexOf(key) < 0)
Alright, so I've actually come up with a function that does exactly what I need. The issue is, I'd like to minify it so that it's not so big. Also, I can't seem to find a way to check if an item is a object even when it's null.
In the end, this works for me. If anyone can improve it that'd be awesome.
function getKeysDeep(obj, arr = []) {
Object.keys(obj).forEach(key => {
if (typeof obj[key] === 'object' && !Array.isArray(obj[key]) && obj[key] !== null) {
arr = this.getKeysDeep(obj[key], arr);
}
});
return arr.concat(Object.keys(obj));
}
getKeysDeep(myobj);

Return object in array with specific property [duplicate]

This question already has answers here:
Find object by id in an array of JavaScript objects
(36 answers)
Closed 4 months ago.
I'm fairly new with writing my own JS functions, and I'm struggling with this one.
I want to run through an array of objects, find an object that matches a particular ID, and then return that object.
So far this is what I have:
var findTeam = function() {
$scope.extraTeamData.forEach(team) {
if(team.team_id === $scope.whichTeam) { return team }
}
$scope.thisTeam = team;
};
$scope.teamDetails is my array, and the $scope.whichTeam variable holds the correct ID which I am checking against.
Ultimately I want to be able to assign the object that results from the function to the $scope.thisTeam variable, so I can call its properties in the view.
Any help would be appreciated.
Thanks.
You could use Array#some which ends the iteration if found
var findTeam = function() {
$scope.extraTeamData.some(function (team) {
if (team.team_id === $scope.whichTeam) {
$scope.thisTeam = team;
return true;
}
});
};
Move your $scope.thisTeam = team; to within the if check.
var findTeam = function() {
$scope.teamDetails.forEach(team) {
if(team.team_id === $scope.whichTeam) {
$scope.thisTeam = team;
}
}
};
$scope.team = $scope.teamDetails.filter(function (team) {
return team.team_id === $scope.whichTeam;
})[0];
You need to use filter method of array. It creates new array elements that match given predicate (function that return boolean value). Then you simply take first value.
You can also use find, but it is not implemented in every browser yet.
It would look something like this:
$scope.team = $scope.teamDetails.find(function (team) {
return team.team_id === $scope.whichTeam;
});

Comparing 2 JSON objects structure in JavaScript

I am using angular-translate for a big application. Having several people committing code + translations, many times the translation objects are not in sync.
I am building a Grunt plugin to look at both files' structure and compare it (just the keys and overall structure, not values).
The main goals are:
Look into each file, and check if the structure of the whole object
(or file, in this case) is the exact same as the translated ones;
On error, return the key that doesn't match.
It turns out it was a bit more complicated than I anticipated. So i figured I could do something like:
Sort the object;
Check the type of data the value contains (since they are translations, it will only have strings, or objects for the nestings) and store it in another object, making the key equal to the original key and the value would be a string 'String', or an object in case it's an object. That object contains the children elements;
Recursively repeat steps 1-2 until the whole object is mapped and sorted;
Do the same for all the files
Stringify and compare everything.
A tiny example would be the following object:
{
key1: 'cool',
key2: 'cooler',
keyWhatever: {
anotherObject: {
key1: 'better',
keyX: 'awesome'
},
aObject: 'actually, it\'s a string'
},
aKey: 'more awesomeness'
}
would map to:
{
aKey: 'String',
key1: 'String',
key2: 'String',
keyWhatever: {
aObject: 'String',
anotherObject: {
key1: 'String',
keyX: 'String'
}
}
}
After this, I would stringify all the objects and proceed with a strict comparison.
My question is, is there a better way to perform this? Both in terms of simplicity and performance, since there are many translation files and they are fairly big.
I tried to look for libraries that would already do this, but I couldn't find any.
Thank you
EDIT: Thank you Jared for pointing out objects can't be sorted. I am ashamed for saying something like that :D Another solution could be iterating each of the properties on the main translation file, and in case they are strings, compare the key with the other files. In case they are objects, "enter" them, and do the same. Maybe it is even simpler than my first guess. What should be done?
Lets say you have two JSON objects, jsonA and jsonB.
function compareValues(a, b) {
//if a and b aren't the same type, they can't be equal
if (typeof a !== typeof b) {
return false;
}
// Need the truthy guard because
// typeof null === 'object'
if (a && typeof a === 'object') {
var keysA = Object.keys(a).sort(),
keysB = Object.keys(b).sort();
//if a and b are objects with different no of keys, unequal
if (keysA.length !== keysB.length) {
return false;
}
//if keys aren't all the same, unequal
if (!keysA.every(function(k, i) { return k === keysB[i];})) {
return false;
}
//recurse on the values for each key
return keysA.every(function(key) {
//if we made it here, they have identical keys
return compareValues(a[key], b[key]);
});
//for primitives just use a straight up check
} else {
return a === b;
}
}
//true if their structure, values, and keys are identical
var passed = compareValues(jsonA, jsonB);
Note that this can overflow the stack for deeply nested JSON objects. Note also that this will work for JSON but not necessarily regular JS objects as special handling is needed for Date Objects, Regexes, etc.
Actually you do need to sort the keys, as they are not required to be spit out in any particular order. Write a function,
function getComparableForObject(obj) {
var keys = Object.keys(obj);
keys.sort(a, b => a > b ? 1 : -1);
var comparable = keys.map(
key => key + ":" + getValueRepresentation(obj[key])
).join(",");
return "{" + comparable + "}";
}
Where getValueRepresentation is a function that either returns "String" or calls getComparableForObject. If you are worried about circular references, add a Symbol to the outer scope, repr, assign obj[repr] = comparable in the function above, and in getValueRepresentation check every object for a defined obj[repr] and return it instead of processing it recursively.
Sorting an array of the keys from the object works. However, sorting has an average time complexity of O(nâ‹…log(n)). We can do better. A fast general algorithm for ensuring two sets A and B are equivalent is as follows:
for item in B
if item in A
remove item from A
else
sets are not equivalent
sets are equivalent iff A is empty
To address #Katana31, we can detect circular references as we go by maintaining a set of visited objects and ensuring that all descendents of that object are not already in the list:
# poorly written pseudo-code
fn detectCycles(A, found = {})
if A in found
there is a cycle
else
found = clone(found)
add A to found
for child in A
detectCycles(child, found)
Here's a complete implementation (you can find a simplified version that assumes JSON/non-circular input here):
var hasOwn = Object.prototype.hasOwnProperty;
var indexOf = Array.prototype.indexOf;
function isObjectEmpty(obj) {
for (var key in obj) {
return false;
}
return true;
}
function copyKeys(obj) {
var newObj = {};
for (var key in obj) {
newObj[key] = undefined;
}
return newObj;
}
// compares the structure of arbitrary values
function compareObjectStructure(a, b) {
return function innerCompare(a, b, pathA, pathB) {
if (typeof a !== typeof b) {
return false;
}
if (typeof a === 'object') {
// both or neither, but not mismatched
if (Array.isArray(a) !== Array.isArray(b)) {
return false;
}
if (indexOf.call(pathA, a) !== -1 || indexOf.call(pathB, b) !== -1) {
return false;
}
pathA = pathA.slice();
pathA.push(a);
pathB = pathB.slice();
pathB.push(b);
if (Array.isArray(a)) {
// can't compare structure in array if we don't have items in both
if (!a.length || !b.length) {
return true;
}
for (var i = 1; i < a.length; i++) {
if (!innerCompare(a[0], a[i], pathA, pathA)) {
return false;
}
}
for (var i = 0; i < b.length; i++) {
if (!innerCompare(a[0], b[i], pathA, pathB)) {
return false;
}
}
return true;
}
var map = copyKeys(a), keys = Object.keys(b);
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
if (!hasOwn.call(map, key) || !innerCompare(a[key], b[key], pathA,
pathB)) {
return false;
}
delete map[key];
}
// we should've found all the keys in the map
return isObjectEmpty(map);
}
return true;
}(a, b, [], []);
}
Note that this implementation directly compares two objects for structural equivalency, but doesn't reduce the objects to a directly comparable value (like a string). I haven't done any performance testing, but I suspect that it won't add significant value, though it will remove the need to repeatedly ensure objects are non-cyclic. For that reason, you could easily split compareObjectStructure into two functions - one to compare the structure and one to check for cycles.

For...in loop filtering only objects

Is there a way to filter out everything inside of a for...in loop to only get the objects?
I'm writing a function to loop through nested objects to find certain pieces of data and then save it to localStorage.
Example:
var equipped = {
data: [the rest of the properties of equipped go here],
tool: {
data: [the rest of the properties of tool go here],
axe: {
data: [the rest of the properties of axe go here],
iron: {...},
steel: {...}
}
}
}
The tool/axe/metal properties are all generated dynamically and is different each time. Inside the metal properties is the data I'm trying to save. I would normally just loop through the array if I was trying to access the data (Using knockoutjs for binding, it's much easier to just foreach the data array), but I'm using the variable from a for...in loop to build the rest of the tree in my localStorage object before stringifying it.
How I'm reading the object:
for (var type in equipped) {
if (check goes here) {
savedValue.equipped[type] = {};
for (var category in equipped[type]) {
etc etc...
}
}
}
I understand that everything is an object type so I can't just do an instanceof or typeof on a defined object to filter them out. Is there another easy way to do it inside of an if statement or do I have to make each step of the tree from a constructor so I can instanceof RealObject?
Either of these should do well:
function isObject(val) {
if (val === null) { return false;}
return (typeof val === 'object');
}
or
function isObject(obj) {
return obj === Object(obj);
}
or
// this only works with object literals
function isObject(val) {
return (!!val) && (val.constructor === Object);
};
this last one, gives me the following:
console.log(isObject()); // false
console.log(isObject([])); // false
console.log(isObject(new Date)); // false
console.log(isObject({})); // true
console.log(isObject(null)); // false
console.log(isObject(true)); // false
console.log(isObject(1)); // false
console.log(isObject('someValueString')); // false
so, something like:
for (var type in equipped) {
if (isObject(type)) {
savedValue.equipped[type] = {};
for (var category in equipped[type]) {
etc etc...
}
}
}
Note: You can also try the following, but I have not used it. So you'd have to go thru your use cases.
Object.getPrototypeOf
Here is the code to check whether the variable is object or not:
function isJsonObject( obj ) {
// Must be an Object.
// Because of IE, we also have to check the presence of the constructor property.
// Make sure that DOM nodes and window objects don't pass through, as well
if ( !obj || obj.toString() !== "[object Object]" || obj.nodeType || obj.setInterval ) {
return false;
}
// Not own constructor property must be Object
if ( obj.constructor
&& !obj.hasOwnProperty("constructor")
&& !obj.constructor.prototype.hasOwnProperty("isPrototypeOf")) {
return false;
}
// Own properties are enumerated firstly, so to speed up,
// if last one is own, then all properties are own.
var key;
for ( key in obj ) {}
return key === undefined || obj.hasOwnProperty( key );
}
There's an old hack for type detection I've used previously.
var classChecker = {}.toString;
classChecker.call({});
classChecker.call(function() {});
classChecker.call([]);
// etc...
// More immediately relevant use:
var savedValue = {
equipped: {}
};
var objectString = classChecker.call({});
for (var type in equipped) {
if (classChecker.call(equipped[type]) === objectString) {
savedValue.equipped[type] = {};
for (var category in equipped[type]) {
// ...
}
}
}
console.log(savedValue);
See http://plnkr.co/edit/nKLQsOdcurrpUCg7cOoJ?p=preview for a working sample. (open your console to view output)

jQuery - how to find a specific JavaScript object inside an array within an object?

I have an object in javaScript:
var stuffObject = {
stuffArray1 : [object1, object2, object3],
stuffArray2 : [object4, object5, object6]
}
object1 to 6 look like this:
object1 = {
dataStuff : {
stuffId: "foobar"
}
}
My question: given the key "foobar", how do I retrieve object1 from the stuffObject using jQuery? The key "stuffId" always has a unique value.
You won't get around iterating over the set to find the object you are looking for. jQuery can't really help with that. Its purpose is DOM manipulation. If you want functionality to deal with objects, sets, lists, etc., check out lodash.
I wrote a function to deal with the problem. I hope it's understandable.
var stuffObject = {
stuffArray1 : [{dataStuff: {stuffId: 'foobar'}}, {dataStuff: {stuffId: 'foo'}}, {}],
stuffArray2 : [{}, {dataStuff: {stuffId: 'bar'}}, {}]
}
function getObjByStuffId(stuffObject, stuffId) {
var key, arr, i, obj;
// Iterate over all the arrays in the object
for(key in stuffObject) {
if(stuffObject.hasOwnProperty(key)) {
arr = stuffObject[key];
// Iterate over all the values in the array
for(i = 0; i < arr.length; i++) {
obj = arr[i];
// And if it has the value we are looking for
if(typeof obj.dataStuff === 'object'
&& obj.dataStuff.stuffId === stuffId) {
// Stop searching and return the object.
return obj;
}
}
}
}
}
console.log('foobar?', getObjByStuffId(stuffObject, 'foobar') );
console.log('foo?', getObjByStuffId(stuffObject, 'foo') );
console.log('bar?', getObjByStuffId(stuffObject, 'bar') );
Thanks for the help guys, using the input of other people I have solved it myself:
getStuffById: function(id){
for (stuffArray in stuffObject) {
for (stuff in stuffObject[stuffArray]) {
if (stuffObject[stuffArray][stuff].dataStuff.stuffId == id) {
return stuffObject[stuffArray][stuff];
}
}
}
return null;
}
This also works better than the (now deleted) answer that uses .grep(), as this function terminates as soon as it finds the correct object.

Categories