Using lodash isEqual() to compare single object property with matching properties from large array with multiple objects - javascript

I've looked at lodash documentation and played around with comparing simple objects. I've also found a number of explanations online for comparing entire objects and other types of comparisons, but I want to compare one property value in a single object with the values of all properties of a certain name in a large array with multiple objects.
Is lodash smart enough to do this as is, and, if so, what would be the proper syntax to handle this? Or do I need some sort of loop to work through the larger object and recursively compare its properties of a certain name with the small object property?
The javascript comparison I'm looking for would be something like this, but I don't know how to indicate that I want to compare all itemURL properties in the large array:
// guard clause to end the larger function if test is true, any match found
if (_.isEqual(feedItem.link, rssDataFileArr.itemURL)) {
return;
}
Small object example:
const feedItem = {
link: 'https://news.google.com/rss/search?q=nodejs',
otherProperty: 'whatever'
}
Large array of objects example:
const rssDataFileArr = [
{
"itemURL": "https://news.google.com/rss/search?q=rss-parser",
"irrelevantProperty": "hello"
},
{
"itemURL": "https://news.google.com/rss/search?q=nodejs",
"irrelevantProperty": "world"
},
{
"itemURL": "https://news.google.com/rss/search?q=javascript",
"irrelevantProperty": "hello"
}
]
Any and all help appreciated.

As per suggestion in comment, I went with a built-in javascript method instead of lodash. I used some() because I only needed a true/false boolean result, not a find() value.
const feedItem = {
link: 'https://news.google.com/rss/search?q=nodejs',
otherProperty: 'whatever',
};
const rssDataFileArr = [
{
itemURL: 'https://news.google.com/rss/search?q=rss-parser',
irrelevantProperty: 'hello',
},
{
itemURL: 'https://news.google.com/rss/search?q=nodejs',
irrelevantProperty: 'world',
},
{
itemURL: 'https://news.google.com/rss/search?q=javascript',
irrelevantProperty: 'hello',
},
{
itemURL: 'https://news.google.com/rss/search?q=nodejs',
irrelevantProperty: 'world',
},
];
const linkMatch = rssDataFileArr.some(
({ itemURL }) => itemURL === feedItem.link
);
// guard clause to end the larger function if test is true, any match found
if (linkMatch) {
console.log('linkMatch is true');
return;
}

Related

How can I create a JavaScript filter function for multiple filters at once

My Problem:
I'm having a website where I can compare products stored inside an array (with objects). I want to add different filters from array inside of an object that get applied together.
For two filters I can easily do it (see my code below). I just compare two objects and use a filter depending on their content.
But what would be a good approach to use the filter if there are more than two objects. Can I loop through the object and compare if the arrays are empty?
With my current approach I would have to extend my code for every new filter and it would balloon.
What I'm trying to do:
I want to check which filter objects have any data in their "feature" array (that array gets filled after the user clicks a filter on the site) and if they have I want to use these arrays to filter the main filteredArray array.
My current Object:
features_collection: {
aspect_ratio_object: {
features: [],
value: "Aspect Ratio",
},
performance_rating_object: {
features: [],
value: "Performance Rating",
},
},
My Filter Function:
if (
features_collection.aspect_ratio_object.features.length &&
features_collection.performance_rating_object.features.length
) {
return filteredArray.filter(
(obj) =>
features_collection.aspect_ratio_object.features.includes(
obj[features_collection.aspect_ratio_object.value]
) &&
features_collection.performance_rating_object.features.includes(
obj[features_collection.performance_rating_object.value]
)
);
} else if (
features_collection.aspect_ratio_object.features.length ||
features_collection.performance_rating_object.features.length
) {
return filteredArray.filter(
(obj) =>
features_collection.aspect_ratio_object.features.includes(
obj[features_collection.aspect_ratio_object.value]
) ||
features_collection.performance_rating_object.features.includes(
obj[features_collection.performance_rating_object.value]
)
);
}
},
Further Notes:
I can also change my object. I could change it into an array of objects if that would make things easier?
Making your filters an array seems more practical. Here's an example on how to
filter a set of objects against your feature_collection.
function filter_by_features(targets, feature_collection) {
// Start right of to filter the `filteredArray`
return targets.filter(obj => {
// go through every feature and test it against the current object.
// every() returns either true or false and the targets array is filtered
// by that condition supplied within the callback of `every()`
return feature_collection.every(filter => {
// If for a given feature no filter is available, return true
// so the test for this filter passes.
if(filter.features.length === 0) {
return true
}
// there are features, check if any applies.
return filter.features.includes(obj[filter.value])
})
})
}
Usage
// feature collection (as array)
const feature_collection = [
{
features: [],
value: "Aspect Ratio",
},
{
features: [],
value: "Performance Rating",
}
]
// the objects you want to filter.
const objects_to_filter = [/* ... */]
const filtered = filter_by_features(objects_to_filter, feature_collection)
docs
every()
You obviously have too loop through your object.
Here is your loop code for features_collection:
features_collection.forEach(function (item, index) {
console.log(item, index);
});

Is there a way to traverse a possibly-self-containing object in JavaScript?

I want to descend an object in Javascript looking for a specific string. Unfortunately, this object is built in such a way that it'd be impossible to simply use the source and Ctrl-F for that string, and it's also built in such a way that recursive functions trying to descend it risk getting trapped inside of it forever.
Basically, this object contains itself. Not just once, but in very many areas. I cannot simply say "exclude these keys", as the object is obfuscated and therefore we'd be here all day listing keys, and once we were done we wouldn't have looked at all the data.
As well, I need to be able to descend __proto__ and prototype, as useful strings are hidden in there too. (But only for functions and objects.)
While I'd prefer something along the lines of findStuff(object, /string/ig), that may be hard, so any function that simply has areas clearly marked that the control flow falls to once it's found specific objects (function, string, etc.)
Thank you, and sorry for such a pain in the butt question.
Edit: In case it helps, I'm trying to traverse a compiled Construct2 runtime object. I'm not going to post the full thing here as it's not going to fit in any pastebin no matter how forgiving, and also I don't want to accidentally post resources I don't have the permission to provide. (Don't worry though, I'm not trying to pirate it myself, I'm simply trying to figure out some user-facing functionality)
You could use a WeakSet to keep track of the objects that were already traversed:
function traverseOnce(obj, cb) {
const visited = new WeakSet();
(function traverse(obj) {
for(const [key, value] of Object.entries(obj)) {
if(typeof value === "object" && value !== null) {
if(visited.has(value)) continue;
visited.add(value);
cb(value);
traverse(value);
}
}
})(obj);
}
Through the WeakSet you got O(1) lookup time, and are also sure that this will never leak.
Usable as:
const nested = { other: { a: 1 } };
nested.self = nested;
traverseOnce(nested, console.log);
// nested: { other, self }
// other: { a: 1 }
You could also use a Symbol to flag traversed objects, for that replace new WeakSet() with Symbol(), visited.has(value) with value[visited] and visuted.add(value) with value[visited] = true;
Any time you're traversing a potentially cyclical object, keeping a memo of already traversed objects and breaking if you've seen the current object before is a standard technique. You can use Set to do so.
Keep a list of objects you have recursed into, and then check each new object against that list.
const data = {
foo: {
bar: 1
},
one: 1,
jaz: {
hello: {
x: 1
}
}
};
data.bar = data.foo;
data.foo.foo = data.foo;
data.jaz.hello.foo = data;
function search_for_1() {
const seen = [];
search(data);
function search(object) {
Object.values(object).forEach(value => {
if (typeof value === "object") {
if (seen.includes(value)) {
console.log("Seen this already");
} else {
seen.push(value);
search(value);
}
} else {
if (value === 1) {
console.log("Found 1");
}
}
});
}
}
search_for_1();
Don't reinvent the wheel There are libraries for this kind of stuff.
We use object-scan for all our data processing. It's very powerful once you wrap your head around it. Here is how it would work for your questions
// const objectScan = require('object-scan');
const traverse = (data) => objectScan(['**'], {
filterFn: ({ key, value, parent }) => {
// do something here
},
breakFn: ({ isCircular }) => isCircular === true
})(data);
const circular = { name: 'Max', age: 5, sex: undefined, details: { color: 'black', breed: undefined } };
circular.sex = circular;
circular.details.breed = circular;
console.log(traverse(circular));
/* =>
[ [ 'details', 'breed' ],
[ 'details', 'color' ],
[ 'details' ],
[ 'sex' ],
[ 'age' ],
[ 'name' ] ]
*/
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan#13.8.0"></script>
Disclaimer: I'm the author of object-scan

Querying data in mongodb using where clause [duplicate]

I want to perform a query on this collection to determine which documents have any keys in things that match a certain value. Is this possible?
I have a collection of documents like:
{
"things": {
"thing1": "red",
"thing2": "blue",
"thing3": "green"
}
}
EDIT: for conciseness
If you don't know what the keys will be and you need it to be interactive, then you'll need to use the (notoriously performance challenged) $where operator like so (in the shell):
db.test.find({$where: function() {
for (var field in this.settings) {
if (this.settings[field] == "red") return true;
}
return false;
}})
If you have a large collection, this may be too slow for your purposes, but it's your only option if your set of keys is unknown.
MongoDB 3.6 Update
You can now do this without $where by using the $objectToArray aggregation operator:
db.test.aggregate([
// Project things as a key/value array, along with the original doc
{$project: {
array: {$objectToArray: '$things'},
doc: '$$ROOT'
}},
// Match the docs with a field value of 'red'
{$match: {'array.v': 'red'}},
// Re-project the original doc
{$replaceRoot: {newRoot: '$doc'}}
])
I'd suggest a schema change so that you can actually do reasonable queries in MongoDB.
From:
{
"userId": "12347",
"settings": {
"SettingA": "blue",
"SettingB": "blue",
"SettingC": "green"
}
}
to:
{
"userId": "12347",
"settings": [
{ name: "SettingA", value: "blue" },
{ name: "SettingB", value: "blue" },
{ name: "SettingC", value: "green" }
]
}
Then, you could index on "settings.value", and do a query like:
db.settings.ensureIndex({ "settings.value" : 1})
db.settings.find({ "settings.value" : "blue" })
The change really is simple ..., as it moves the setting name and setting value to fully indexable fields, and stores the list of settings as an array.
If you can't change the schema, you could try #JohnnyHK's solution, but be warned that it's basically worst case in terms of performance and it won't work effectively with indexes.
Sadly, none of the previous answers address the fact that mongo can contain nested values in arrays or nested objects.
THIS IS THE CORRECT QUERY:
{$where: function() {
var deepIterate = function (obj, value) {
for (var field in obj) {
if (obj[field] == value){
return true;
}
var found = false;
if ( typeof obj[field] === 'object') {
found = deepIterate(obj[field], value)
if (found) { return true; }
}
}
return false;
};
return deepIterate(this, "573c79aef4ef4b9a9523028f")
}}
Since calling typeof on array or nested object will return 'object' this means that the query will iterate on all nested elements and will iterate through all of them until the key with value will be found.
You can check previous answers with a nested value and the results will be far from desired.
Stringifying the whole object is a hit on performance since it has to iterate through all memory sectors one by one trying to match them. And creates a copy of the object as a string in ram memory (both inefficient since query uses more ram and slow since function context already has a loaded object).
The query itself can work with objectId, string, int and any basic javascript type you wish.

How to update the value of a single property within a state object in React.js?

So I have the following object structure:
const SamplePalette = {
id: 1,
name: "Sample Palette",
description: "this is a short description",
swatches: [
{
val: "#FF6245",
tints: ["#FFE0DB", "#FFA797"],
shades: ["#751408", "#C33F27"]
},
{
val: "#FFFDA4",
tints: ["#FFFFE1"],
shades: ["#CCCB83"]
},
{
val: "#BFE8A3",
tints: ["#E7FFD7"],
shades: ["#95B77E"]
}
]
}
Let's imagine that this object is managed by the state of my app like this:
this.state = {
currentPalette: SamplePalette,
}
My question is how would I go about updating the val property of a given swatch object in the swatches array? Or more generally - how do I only update pieces of this object?
I tried using the update helper as well as to figure out how Object.assign() works, however I've been unsuccessful and frankly can't really grasp the syntax by just looking at examples.
Also, since I'm going to be modifying this object quite a lot, should I look into maybe using Redux?
[EDIT]
I tried #maxim.sh suggestion but with no success:
this.setState(
{ currentPalette: {...this.state.currentPalette,
swatches[0].val: newValue}
})
Consider you have new new_swatches
I think the clearer way is to get array, update it and put back as:
let new_swatches = this.state.currentPalette.swatches;
new_swatches[0].val = newValue;
this.setState(
{ currentPalette:
{ ...this.state.currentPalette, swatches: new_swatches }
});
Also you have : Immutability Helpers or https://github.com/kolodny/immutability-helper
Available Commands
{$push: array} push() all the items in array on the target.
{$unshift: array} unshift() all the items in array on the target.
{$splice: array of arrays} for each item in arrays call splice() on the target with the parameters provided by the item.
{$set: any} replace the target entirely.
{$merge: object} merge the keys of object with the target.
{$apply: function} passes in the current value to the function and updates it with the new returned value.

Check if key/value pair within an array exists using Underscore JS _contains method

If I have the following object:
var record = {
title: "Hello",
children: [
{
title: "hello",
active: true
},
{
title: "bye",
active: false
}
};
I want to use underscore to determine if one of the children within the record has or does not have a title equal to a variable that will come from a form post, but also needs to be case insensitive... So for example:
var child = { title: "heLLo", active: true }
And underscore ( and this is wrong, and what I need help with ):
if ( _.contains(record.children, child.title) ) {
// it already exists...
} else {
// ok we can add this to the object
}
So basically I don't understand how to do this with underscore when dealing with array objects that have multiple key/value pairs. Also what is the best method for ignoring case? Should this be done in the underscore _.contains function? Regex? Use toLowerCase() beforehand to create the variables? If someone types in any variation of "Hello", "HELLO", "heLLO", etc. I don't want the insert to take place.
Thank you!
Use _.find and RegExp with "i" case-ignore flag
var valueFromPost = "bye";
var someOfChildrenHasValueFromPost = _.find(record.children,function(child){
return child.title.match(new RegExp(valueFromPost,"i"));
});
Update
Here is an example #JSFiddle
JS code:
record = {
children:[
{title:'bye'},
{title:'Bye'},
{title:'Hello'}
]
}
var testValue = function(value) {
return _.find(record.children,function(child){
return child.title.match(new RegExp(value,"i"));
});
}
console.debug(testValue('Bye')); //returns object with "Bye" title
console.debug(testValue('What'));//returns undefined
console.debug(testValue('bye')); //returns object with "bye" title

Categories

Resources