Is there a better/shorter way to check a value of an object within an array which is part of an object in Javascript? - javascript

I'm sure this question must have been answered before, so a link (or what specifically to ask Google) would suffice, but what is the best way to do a check in a scenario like this (and does the new ? operator help in scenarios like this):
/**
* An API returns a job object like:
* { id: 123, name: 'The Job', details: [ { detail_name: "Foo", some: "thing" }, { detail_name: "Bar", some: "thing else" } ] }
*/
const fooDetail = job.details.find(attr => {
return attr.detail_name === 'Foo' });
if (fooDetail && fooDetail.detail_name === "Foo") {
// todo process `some: "thing"`
}
It seems long winded to have to find an object in an array based on a property, but then having to check again that the object exists before checking the property (or get a can't get detail_name of undefined error). Is there a better/shorter way?

You can use Array.some
This will return true if one is found..
The same callback as find can be used
const hasDetail = job.details.some(attr => {
return attr.detail_name === 'Foo' });
if (hasDetail) {
// todo process `some: "thing"`
}
If you're simply want to get the value and then later on check if the property exists, you can use find and ?. operator
const addDetail = job.details.find(attr => {
return attr.detail_name === 'Foo' });
if (addDetail?.detail_name === 'Foo') {
// todo process `some: "thing"`
}

You can do also
job.details.forEach(x=>{
if(x.detail_name == "Foo") {
console.log(x.some);
}
});

Related

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`

How to customize assertions in Cypress

I need to test that an array of object contains a certain value. The test is written with Cypress, and for that, I use cy.wrap and .some().
My code looks like this:
const myCustomArray = [{ name: 'Lisa' }, { name: 'Katie' }];
cy.wrap(myCustomArray.some((user) => {
if (user.name === 'Lisa') {
return true;
} else {
return false;
}
})).should('eq', true);
This works well, but the problem is that it then returns me a very non-specific message in the Cypress console.
What I would like to have, is to change my code in a way that the message would be understandable. In the idea, it would be something like that:
const myCustomArray = [{ name: 'Lisa' }, { name: 'Katie' }];
cy.wrap(myCustomArray.some((user) => {
if (user.name === 'Lisa') {
return 'user name is Lisa';
}
})).should('eq', 'user name is Lisa');
But this can't work as .some() can only return a boolean. I suppose there is an array function that could help me do that, but I can't find which one.
I am not sure whether:
There are Cypress commands I am unaware of that could solve this issue, eg. customizing the assertion message.
Or it can just be solved with using JavaScript
Both solutions would be fine for me.
How about using .find() instead of .some(), and deep-eq the result,
cy.wrap(myCustomArray.find(user => user.name === 'Lisa'))
.should('deep.eq', { name: 'Lisa' });
ASSERT expected { name: Lisa } to deeply equal { name: Lisa }
or if you have big objects and just want to see the name,
cy.wrap(myCustomArray.map(user => user.name).find(name => name === 'Lisa'))
.should('eq', 'Lisa');
ASSERT expected Lisa to equal Lisa

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

Safely access a property inside a nested javascript object

The object structure is as follows.
object = {
obj1: {
obj2: {
name: 'MY name'
}
}
}
This structure is dynamic and sometimes there wont be an obj1.
So, In react you will be writing the code like..
object && obj1 && obj2 && obj2.name
so that only if object, obj1, and obj2 are present then obj2.name will be displayed.
In this case there wont be any undefined error since the presence of each object is checked prior to going inside the function.
Is there alternate way to the above code so that it displays the name when all the objects are present.
If not, it should not throw an error.
Use hasOwnProperty to check if obj1 is present or not
object = {
obj1: {
obj2: {
name: 'MY name'
}
}
}
if(object.hasOwnProperty('obj1'))
console.log(object.obj1.obj2.name)
else
console.log(object.obj2.name)
Try
object = {
obj1: {
obj2: {
name: 'MY name'
}
}
}
var name = (object.hasOwnProperty('obj1')) ? object.obj1.obj2.name : object.obj2.name;
console.log(name);
You need to check every property before reading a nested one:
const name = object && object.obj1 && object.obj1.obj2 && object.obj1.obj2.name;
This is laborious and verbose as you have to repeat the same thing over and over to access deeply nested properties.
I suggest to use a safeEval function that wraps your potentialy dangerous property access code with a try/catch and returns undefined if an error occurs.
This is much shorter than manually checking every property:
const name = safeEval(() => obj.obj1.obj2.name);
Here is an example:
const obj = { obj1: { obj2: { name: 'Hello' } } }
function safeEval(fn) {
try { return fn(); }
catch { return undefined; }
}
const a = obj && obj.obj1 && obj.obj1.obj2 && obj.obj1.obj2.name;
const b = safeEval(() => obj.obj1.obj2.name);
const c = safeEval(() => obj.obj1.obj2.obj3.obj4.name);
console.log(a, b, c);
I would recommend writing a recursive function that inspects the object and recurses down its children if a name is found. I see others suggested catching the error- I would advise against abusing exception handling for runtime errors like this.

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