How do I determine a Keypath for a given key and iterable? - javascript

I have an Immutable.js Map that looks something like this:
item1: {
prop1: {
potato: true,
turnip: false,
ragamuffin: true
}
},
item2: {
prop1: {
petunia: true,
azalea: false,
stinkweed: true
}
}
I am writing a function that'll take a keyName and newValue. Instead of enumerating each keypath, I'd like to be able to do something like:
state = state.setIn(state.getKeyPath(keyName), newValue)
For this project, duplicate key names are not a concern (i.e., no Map I parse will have >1 instance of any keyName)
ETA: my current solution is OK for now b/c my state will never be nested >1 level deep, but would rather use a built-in method if one exists:
let keyName = 'petunia'
let keyPath
// make a List of all top-level keys, then loop through each
List(sampleMap.keys()).forEach((topLevelKey) => {
// if we find our key, assign our keyPath accordingly
if (sampleMap.hasIn([topLevelKey, keyName]) === true) {
keyPath = [topLevelKey, keyName]
})
})

Related

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

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;
}

chain logical AND without fixed length given an array in Node.js and Typescript

Scenario:
I am making a generic function that returns a boolean depending on logical AND statements, however, the function being generic accept multiple type of objects and arrays, and the statements can vary depending on the objects.
at the moment I have something like this
private async myFunction(
myArray: myArrObj[],
myObj : myObj,
): Promise<boolean> {
return (
myArr.some(
(a) =>
a.status1=== "*" ||
a.status1 === myObj.status1.status1Id
) &&
myArr.some(
(a) =>
a.status2=== "*" ||
a.status2 === myObj.status2.status2Id
) &&
myArr.some(
(a) =>
a.status3=== "*" ||
a.status3 === myObj.status3.status3Id
) &&
myArr.some(
(a) =>
a.status4=== "*" ||
a.status4 === myObj.status4.status4Id
)
)
}
the issue is not being able to know what kind of array is passed and how many checks are needed, how can I make a return? My idea was storing each array.some method in an array and join them with " && ", this approach would require to execute something from a string, which I'm not sure is the most secure thing to do, since eval is not secure at all.
to get the myObj statuses I could just use a for loop and store the the the property in a string.
I can't come up with a good solution, so feel free to propose something new if my idea is not good enough
As noted by others in the comments, it would help if you had a reproducible example with sample data. That being said, from your comment:
but the statuses and id's have different names, some id's are .nameId, and some are just .id , but the statuses themselves have the same name, so instead of status1 and obStatus1 it really should be status1 and status1
Breaking this down:
but the statuses and id's have different names, some id's are .nameId, and some are just .id
You could try to see if nameId exists and fall back to id.
but the statuses themselves have the same name, so instead of status1 and obStatus1 it really should be status1 and status1
When myArr entries share keys with myObj, then you could simply loop through myObj's keys.
async function myFunction(myArr, myObj) {
// Fallback value for if .nameId and .id both don't exist.
// Falling back to `undefined` would cause a bug / false positives.
const notFound = Symbol();
// Loop through every key:value pair in the input object.
return Object.entries(myObj).every(([myObjKey, myObjValue]) => {
// Handle both `.nameId` and `.id`
const id = myObjValue[`${myObjKey}Id`] ?? myObjValue.id ?? notFound;
// If `myArrObj`'s children only ever contain exactly
// a single key { status2: { someRandomKey: 123 } }, then you
// could use myObjValue[Object.keys(myObjValue)[0]];
// For this key--for example "status1"--is there *any* array entry
// in `myArrObj` that has the same key and value or "*"?
return myArr.some((a) => {
return a[myObjKey] === '*' || a[myObjKey] === id;
});
});
}
With the following sample data:
const sampleArr = [
{ status3: "*" },
{ status2: 234 },
{ status1: 123, thisIsAnUnusedKey: true },
{ status4: 456 },
{ name: "Foobar" },
{ thisIsAnUnusedArrayEntry: true },
];
const sampleObj = {
status1: {
status1Id: 123,
},
status2: {
status2Id: 234,
},
status3: {
status3Id: 345,
},
status4: {
// Different key
id: 456,
},
name: {
// Different dataType
nameId: "Foobar"
}
};
myFunction(sampleArr, sampleObj).then(console.log); // Logs `true`

Shallow copy JavaScript object without references

How can I shallowly copy an JavaScript object and get rid of all non-primitive values (all references), while keeping all properties of the given object. Values of the properties might turn to null in this process.
Object.assign, lodash clone and the spread operator allow us to get a shallow copy of an object. However, despite the naming, the result object is not shallow [fact]. It has copied all references too, so the whole object tree is still accessible.
For a analytics solution, I need to get rid of anything deeper than one level.
How can I do it (libraries are also ok, ES6 is fine) without writing dozens of rules to deal with all the possible data types? Ideally, objects and array properties are not lost, but replaced with something, e.g. null or empty objects/arrays.
Example
const source = {
nr: 1,
str: 'ok',
obj: {
uhOh: 'kill me'
},
arr: ['well ok', { uhOh: 'uhOh' }],
}
// apply voodoo
const expected = {
nr: 1,
str: 'ok',
obj: {},
arr: [],
}
// This would also be an valid result:
const expected = {
nr: 1,
str: 'ok',
obj: null,
arr: null,
}
You could loop through the keys of the object using for...in. If the value is an object, set the key to null in expected, else set the value in expected to the value from source
const source = {
nr: 1,
str: 'ok',
obj: {
uhOh: 'kill me'
},
arr: ['well ok', {
uhOh: 'uhOh'
}],
}
const expected = {};
for (const key in source) {
if (typeof source[key] === 'object')
expected[key] = null
else
expected[key] = source[key]
}
console.log(expected)
This is not an answer in it's own right, but an addendum to the excellent answer by #adiga, this time using typescript and a type parameter:
private primitiveClone<T>(source: T): T {
const dto = Object.assign({}, source);
for (const key in dto) {
if (typeof dto[key] === 'object') {
dto[key] = null;
}
}
return dto;
}
usage
var simpleClone = primitiveClone(data);

Is there a nice way in javascript to removing Falsy values from a javascript object (not an array)?

In JavaScript you have the nice .filter method to remove null or falsy values from arrays. So far I haven't been able to find a method to remove the same from JavaScript Objects.
Why would this be?
Currently you can create a function for arrays like :
function stripNulls(arr) {
return arr.filter(Boolean);
}
Is there a similar function that can be created for JS Objects, or is the way filter works not practical on JS Objects.
The answer to "can I do x to an object" (or an array for that matter) is usually "yes" and it frequently involves some form of reduce.
If you want to filter falsy values you could do something like this:
function filterFalsy(obj) {
return Object.keys(obj).reduce((acc, key) => {
if (obj[key]) {
acc[key] = obj[key]
}
return acc
}, {})
}
const testObj = {
a: 'test',
b: 321,
c: false
}
console.log(filterFalsy(testObj))
This returns a new object without falsy values and leaves the existing object alone.
WARNING: There are better answers provided here. Also, thanks to comments made below user's should be warned using delete may provide suboptimal performance.
Filtering invalid values is a little more complex in objects. At face value this will do what you want:
var arr = [ 'apple', 43, false ];
var trueArr = arr.filter(Boolean);
console.log(trueArr);
var obj = { 'title': 'apple', 'id': 43, 'isOrange': false, 'test': 'asd' };
Object.keys(obj)
.filter(key => !obj[key])
.forEach(key => delete obj[key]);
console.log(obj);
However, this will not iterate over child objects / functions. This logic also directly modifies the original object (which may or may not be desired).
That can easily changed by adding this logic to a function like so:
function removeFalseyProperties(obj) {
Object.keys(obj)
.filter(key => !obj[key])
.forEach(key => delete obj[key]);
return obj;
}
var testObj = { 'title': 'apple', 'id': 43, 'isOrange': false, 'test': 'asd' };
var trutheyObj = removeFalseyProperties(testObj);
console.log(trutheyObj);
falsy values are 0, undefined, null, false, etc.
myArray
.map(item => {
// ...
})
// Get rid of bad values
.filter(Boolean);
By passing Boolean we can remove all the falsy values.

Object: Deep omit

Is there a way to use _.omit on nested object properties?
I want this to happen:
schema = {
firstName: {
type: String
},
secret: {
type: String,
optional: true,
private: true
}
};
schema = _.nestedOmit(schema, 'private');
console.log(schema);
// Should Log
// {
// firstName: {
// type: String
// },
// secret: {
// type: String,
// optional: true
// }
// }
_.nestedOmit obviously doesn't exist and just _.omit doesn't affect nested properties, but it should be clear what I'm looking for.
It also doesn't have to be underscore, but in my experience it often just makes things shorter and clearer.
You could create a nestedOmit mixin that would traverse the object to remove the unwanted key. Something like
_.mixin({
nestedOmit: function(obj, iteratee, context) {
// basic _.omit on the current object
var r = _.omit(obj, iteratee, context);
//transform the children objects
_.each(r, function(val, key) {
if (typeof(val) === "object")
r[key] = _.nestedOmit(val, iteratee, context);
});
return r;
}
});
and a demo http://jsfiddle.net/nikoshr/fez3eyw8/1/
Detailed solution of this issue is posted in another thread. Please have a look at the below thread
Link - Cleaning Unwanted Fields From GraphQL Responses

Categories

Resources