JS: Handle with find() property undefined - javascript

I have a method which returns a value from an element in the array. Not all the elements have the property I want to return. I would like to do this function with one line using the method find(). I've tried this way to solve it:
getExecsFromTour(tourId){
return this.repInfo.find(el => el.id == tourId ).execs || [];
}
But the elements which don't contain the property execs return an error of undefined.
To solve it, I had to store the result in a local variable:
getExecsFromTour(tourId){
let items = this.repInfo.find(el => el.id == tourId);
return items != undefined ? items.execs : [];
}
But I would like to know if I am missing something and this function can be achieved with one sentence.

You seem to have the general idea, Array.prototype.find will search the array for the first element which, when used as an argument of the callback, will have the callback return a truthy value. If nothing is found, it returns undefined.
Your code should work, but yes, one way to do it in one line (if you want) is to use:
getExecsFromTour(tourId){
return (this.repInfo.find(el => el.id == tourId) || {}).execs || [];
}
If Array.prototype.find returns undefined, the first inner parenthetical expression will be evaluated to empty object, which can attempt (and fail) to access the .execs key without a TypeError, which will also evaluate to undefined, in which case the function returns empty array, which is what your code above does.
EDIT: Someone commented this solution already, lol, but as the comments say, nothing wrong with keeping it multiline (more readable that way).

what about
getExecsFromTour(tourId){
return this.repInfo.find(el => 'execs' in el && el.id == tourId ).execs || [];
}
...
EDITED
var a = [{execs : 1, id:4}, {id:5}];
function getExecsFromTour(tourId, x){
return (x = a.find(el => 'execs' in el && el.id == tourId )) ? x.execs : [];
}
this time at least I ran it couple of times

Related

Arguments Object

i'm currently learning js and need to pass a test, every requirement checks out besides "should work on an arguments object".
So I need to use arguments[0]or[1], its also necessary for me to check if the array is an array. Issue here is that for some reason the Array.isArray() part of the code turns my "should work on an arguments object" requirement false, even though I used arguments[0].
please try to ignore the rest of the code, there are also other requirements set. I know they seem kind of unnecessary to include.
this is my code:
function (array, n) {
var resultArray = [];if (typeof arguments[1] !== "number" || arguments[1] == 0 || arguments[1] < 0){
resultArray.push.call(resultArray, arguments[0][0])
return resultArray
}
else if (arguments[1] > arguments[0].length){
return arguments[0] && array
} else {
return resultArray;
}
};
That's because the requirements expects you use somewhere the variable argument array. Instead you use arguments[0].
So use the first over the latter.
Likewise, use n instead of arguments[1]
You can use Array.prototype.slice function simply
const first = (array = [], n = 1) =>
Array.isArray(array) ? array.slice(0, array.length >= n ? n : 1) : [];
If n > array.length this will return the whole array.
Try this:
if (!window[atob('QXJyYXk=')][atob('aXNBcnJheQ==')](arguments[0])) return [];

how to group an array based on a specific function?

Here, I am new to JavaScript. I am solving questions; however, I am having a problem understanding chaining more than one method together. I have been trying to understand this solution, but it took me a lot of time, and I still don't get it.
I understand that I will input the array that I needed to change according to the specific function, which I opted. I understand all of methods functions, but I don't understand their syntax here, so can someone please explain each step to me ?
const group_By = (arr, fn) =>
arr.map(typeof fn === 'function' ? fn : val => val[fn]).reduce((acc, val, i) => {
acc[val] = (acc[val] || []).concat(arr[i]);
return acc;
}, {});
In as few words as possible.
Firstly they compute a ternary expression, here they are checking if the input is a function, if it is they pass it as is, otherwise they create an anonymous function that tries to access the given property. The arrow function after the colon can seem a little confusing but it's still just a function. It takes one argument called val, and returns property which key is inside the fn variable.
typeof fn === 'function' ? fn : val => val[fn]
The next step is to create a new array with new values for each of the elements. Output of this step is just a list of values to group elements on.
For instance calling it on array ["a", "bb"] with a fn='length' would return [1,2]
arr.map(typeof fn === 'function' ? fn : val => val[fn])
Then they call the .reduce function on the output array. The purpose of the reduce function is to create a single value out of all the elements slowly iterating over it. You can tell that the last step returns accumulator value back, and that it is passed as a first argument to the function called on the next element. The empty object at the end is just an initial value of the accumulator.
.reduce((acc, val, i) => {
...
return acc;
}, {});
And finally for the step that does the accumulation. Here firstly the val from the result of the map, is used to access property of the newly created object. If the value does not exist it replaced with an empty array || []. That has the element of the initial array at the same index concatenated onto it. If there were some elements it just adds new ones to it and reassigns the value.
acc[val] = (acc[val] || []).concat(arr[i]);
Okay, what I understood from your query is that you are trying to chain multiple functions together.
function Chained() {
// this reference.
const _this_ = this
this.Function1 = () => // do something and return _this_
this.Function2 = () => // do something here and return _this_
}
Above you can see that chain is a simple object which returns "this" as context. and on context, we already have Function1 and Function2. so due to this following will be valid.
const _ExecuteChained = new Chained()
// Now you can run _ExecuteChained.Function1().Function2() and so on.
Now coming to your code.
const group_By = (arr, fn) =>
arr.map(typeof fn === 'function' ? fn : val => val[fn]).reduce((acc, val,
i) => {
acc[val] = (acc[val] || []).concat(arr[i]);
return acc;
}, {});
Here you are just running a loop on arr and validating if the second param is a function or not if function then return it as is (Since you are using a map it will be stored at the given index. else just get the value of the function at the given key and return it.
Reduce.
in Reduce you are trying to accumulate a given value with (contact or merge or extend) value at a given index of arr in this case function.

Typescript/Javascript assign and return one-liner

Many times I ask myself the same question... With all that syntaxes (not always intuitive) to write quite direct code in JS, I was wondering, would someone know about a one-liner for that kind of operation?
var setFeatured = entry => {
entry.isFeatured = true;
return entry
}
SomeCallThatReturnsAPromise.then(entries => entries.map(setFeatured))
To assign a property and return the object in one shot, that I could put in a readable way directly as arg of entries.map
To give a feedback about what was proposed to me, the common answer was to return a result with a OR operator, after an assignation or function call (which returns undefined, null, false, never, well anything that will trigger the part after the OR):
return entry.isFeatured = true || entry
The interest of my question was to know if I could take advantage of a more compact syntax:
SomeCallThatReturnsAPromise()
.then((entries:EntryType[]) => entries
.map(entry => entry.isFeatured = true || entry)
.filter(entry => entry.something == true))
.then((entries:EntryType[]) => someCallThatReturnsNothingButHasToBeDoneThere() || entries)
.then((entries:EntryType[]) => console.log(entries))
would be easier to read than:
SomeCallThatReturnsAPromise
.then((entries:EntryType[]) => entries
.map(entry => {
entry.isFeatured = true;
return entry;
})
.filter(entry => entry.something == true))
.then((entries:EntryType[]) => {
someCallThatReturnsNothingButHasToBeDoneThere();
return entries;
})
.then((entries:EntryType[]) => console.log(entries))
Notes:
1) I try to avoid creating a function for that. My question was motivated by curiosity and just concerns what Vanilla ES6 or 7 syntaxes have to offer.
2) I was answered to use .forEach rather than .map. I design my code with a functional approach (hence the importance of compact callbacks), so .forEachis not necessarily a good choice for me (and apparently it doesn't have advantages over map in terms of performance or memory consumption). A one-line syntax is convenient both when handling promises callbacks or chains of array functions...
3) the returned type when using the OR operator is a union type, EntryType|null. So it breaks the typing for the subsequent calls and implies a type assertion:
SomeCallThatReturnsAPromise()
.then((entries:EntryType[]) => entries
.map(entry => (entry.isFeatured = true || entry) as EntryType)
.filter(entry => entry.something == true))
.then((entries:EntryType[]) => (someCallThatReturnsNothingButHasToBeDoneThere() || entries) as EntryType[])
.then((entries:EntryType[]) => console.log(entries))
That's getting heavier... I still don't know if I'll use that or stick with the two lines including the return statement.
4) That's a reduced example. I know that my first then contains a synchronous call or that my example could be more accurate.
entries.forEach( (entry) => entry.isFeatured = true );
No need to define the function separately.
Furthermore, as your elements are objects and are handled by reference, one can replace map() by forEach(), which removes the necessity to return a value.
(using map() you would end up with two arrays consisting of the same elements, which probably is not, what you need)
You can do what #Sirko wrote but return it like so:
SomeCallThatReturnsAPromise.then(entries => entries.forEach(entry => entry.isFeatured = true) || entries)
There's no need to use map, instead using forEach will give you a "one-liner", but then you want to return the same value that you received, using logical or (||) you can do that.
although #Sirko is right and, in that specific case, forEach has more sense than using map I think the OP was asking a generic question.
So, in general, how do you assign a property and then return the whole object? this is my suggestion:
function mutateObject(element, value) {
return (element.prop = value) && element || element
}
var object = {prop:1}
var returned = mutateObject(object, 2)
console.log(returned)
How does it work? the first part (element.prop = value) assigns the value to the property and return the value to the expression.
If the value returned is falsy, the value of the || clause is returned. If it's truthy the value of the && is returned.
In this case we return the element itself both times to be sure that's the object it will always be returned, no matter what we set in the property.
Another way to write that is (element.prop = value) ? element : element but, with this syntax, it looks more like a typo with the assignment instead of the comparison so I like the other syntax better.
//Example 1
const gimmeSmile = {}
console.log({ ...gimmeSmile, smile: ":)" })
// Example 2
const smiles = [{},{},{}]
.map(obj => ( { ...obj, smile: ":)"} ));
console.log(smiles)

Javascript: testing for at least one non-empty array (if one of the arrays may be null)

Given two arrays myArray1 and myArray2, which may be null, how can I output a Boolean which tells me if at least one array is non-empty?
Assuming that I have the following variables:
myArray1 = ["one", "two", "three"]; //-> non-empty array
myArray2 = null; //-> not an array (in my case this happens from .match() returning no results)
I want an expression, such that myArray1 && myArray2 will be FALSE, but myArray1 || myArray2 will be TRUE.
I did look through other relevant Stack Overflow questions (see an abridged list below), but since I still struggled to figure out the solution, I thought I would post it as a separate question since answers might also benefit others.
The common way of testing for empty arrays is:
myBooleanOr = (myArray1.length || myArray2.length); //3
myBooleanAnd = (myArray1.length && myArray2.length); //Error
This works if both variables are arrays, but in this case, the second one will throw up Error: cannot read property length of 'null'. Using the Boolean() function does not solve the problem since the following also throws up the same error:
myBooleanAnd = (Boolean(myArray1.length) && Boolean(myArray2.length)); //error
A solution for testing empty arrays which was accepted in several Stack Overflow questions is to use typeof myArray !== "undefined", but that still does not solve the problem, because neither of the arrays match "undefined", so myBooleanAnd will still throw up an error:
var bool = (typeof myArray1 !== "undefined"); //true
var bool = (typeof myArray2 !== "undefined"); //true
var myBooleanAnd = ((typeof myArray1 !== "undefined" && myArray1.length) || (typeof myArray2 !== "undefined" && myArray2.length)); //Error: cannot read property length of null
Comparing the arrays against [], which also seems intuitive, also doesn't work, because neither of the arrays match []:
var bool = (myArray1 !== []); //true
var bool = (myArray2 !== []); //true
Other relevant posts
A number of other questions on Stack Overflow deal with testing for empty Javascript arrays, including:
Testing for empty arrays: Check if array is empty or exists
Testing for empty arrays (jQuery): Check if array is empty or null
Relative advantages of methods for testing empty arrays: Testing for an empty array
Testing for empty objects: How do I test for an empty JavaScript object?
And there are also questions about the truth value of empty arrays in Javascript:
JavaScript: empty array, [ ] evaluates to true in conditional structures. Why is this?
UPDATE
I have corrected the following errors posted in the original question (thanks to those who pointed them out). I am listing them here since they might also be helpful to others:
==! changed to !==
typeof x === undefined, changed to typeof x === "undefined"
I would suggest using a helper function to determine if a single array is non-empty, and then use that twice. This is simple and straightforward:
function isNonEmptyArray(arr) {
return !!(Array.isArray(arr) && arr.length);
}
var myBooleanAnd = isNonEmptyArray(myArray1) && isNonEmptyArray(myArray2);
var myBooleanOr = isNonEmptyArray(myArray1) || isNonEmptyArray(myArray2);
Ok, so, there're a bunch of errors in the code examples, i'll try to to explain them all:
myBooleanOr = (myArray1.length || myArray2.length); //3
myBooleanAnd = (myArray1.length && myArray2.length); //Error
Here, the first line returns the first truthy value it encounters. Since myArray1 has a length > 0, it returns that value and never evaluates the second part of the condition, that's why you're not getting the error. Swap the checks and it will break.
The second line combines the two values to give a result, so it will always give an error when one of the two variables are null.
var bool = (typeof myArray1 === undefined); //false
typeof returns a string, if you compare it to the undefined constant it will always be false, the correct statement is typeof myArray1 === "undefined" as written in most of the posts you linked
var bool = (myArray2 ==! null);
the "strictly not equal" operator is !== and NOT ==!. You're doing a different operation and that's why you get surprising results.
Putting the right spaces in the syntax, this is your code var bool = (myArray2 == !null);
So you boolean-flip the value of null, which is falsy by nature, getting true, and then compare if myArray2 is loosely-equal to true ... since myArray2 is null, and that is falsy as we said, the comparison gives back a false.
That said, for the solution to the question, I'd propose a slightly longer syntax that is more explicit, clear to understand, and you can scale to check how many arrays you like without adding more complexity:
var myArray1 = [1,2,3]
var myArray2 = null
var arrays = [myArray1, myArray2]
var oneNotEmpty = arrays.some( a => typeof a != "undefined" && a != null && a.length > 0)
console.log("At least one array non-empty?", oneNotEmpty)
You could use isArray to check something is an array or not. Most modern browsers supports it. https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray
And then you can combine isArray and array's length to check if something is a valid non empty array or not.
function isOneNotEmpty(arrayOne, arrayTwo){
return (Array.isArray(arrayOne)? arrayOne.length > 0 : false) || (Array.isArray(arrayTwo)? arrayOne.length > 0 : false)
}
console.log(isOneNotEmpty([],null));
console.log(isOneNotEmpty([1,2,3,4],null));
console.log(isOneNotEmpty([3,4,5],[1]));
Testing for an array is simple enough:
var blnIsPopulatedArray = (myArray != null
&& typeof myArray == "object"
&& typeof myArray.length == "number"
&& myArray.length > 0);
Will return false if 'myArray' isn't an array with at least one item, or true if it is an array with at least one item.
One solution is the following (with or without Boolean()):
Using Array.isArray() and array.length:
var myBooleanAnd = Boolean(Array.isArray(myArray2) && myArray2.length) && Boolean(Array.isArray(myArray1) && myArray1.length) ; //false -> OK
var myBooleanOr = Boolean(Array.isArray(myArray2) && myArray2.length) || Boolean(Array.isArray(myArray1) && myArray1.length) ; //true -> OK
It is also possible to use myArray1 !== null instead of Array.isArray(myArray1), but since the latter is a more specific test, the broader Array.isArray() method seems preferable.
UPDATE
I had previously suggested using the following:
var myBooleanAnd = Boolean(myArray1 && myArray2); //returns false -> OK
var myBooleanOr = Boolean(myArray1 || myArray2); //returns true -> OK
but since, as pointed out by #JLRishe, the following expression also returns TRUE, this is not a safe solution, since it will only work in situations where the arrays can never be empty.
var bool = Boolean([] && []); //returns true -> false positive
function arrayIsEmpty(array) {
if (!Array.isArray(array)) {
return false;
}
if (array.length == 0) {
return true;
}
}

How to replace a dynamically specified attribute in JS object

The attribute(or the nested object) is selected dynamically based on conditions. It can be one of the 4 possibilities as follows:
var tempData = o.title ? o["properties"] || o["items"]["properties"] : o[k]["properties"] || o[k]["items"]["properties"];
Then I get this new data, I want to replace the above selected with.
var newData = //some new Object
I want to replace whatever above selected with the new data. I could do the following (go through the condition again and set the new data):
if(o.title){
if (o["properties"]) {
o["properties"] = newData;
} else if (o["items"]["properties"]) {
o["items"]["properties"] = newData;
}
}else{
if (o[k]["properties"]) {
o[k]["properties"] = newData;
} else if (o[k]["items"]["properties"]) {
o[k]["items"]["properties"] = newData;
}
}
But it doesn't look good. What is the more sophisticated way of achieving this?
It is unclear if you are generically attempting to replace any properties property with the newData, or if you are wanting it to specifically be one of the ones you have specified in your code. I have assumed that you are only wanting to replace the ones you specifically have shown in your code.
Note: The following assumes that it is not possible for the value of the properties property to evaluate to false. If it is possible for it to have a value that evaluates to false, this will fail.
As a first pass, I would do something like:
var p;
if (o.title) {
p=o;
} else {
p=o[k];
}
if (p.properties) {
p.properties = newData;
} else if (p.items.properties) {
p.items.properties = newData;
}
However, that relies on:
o is not null or undefined.
o.title does not evaluate to false, if you are trying to test for the existence of o.title.
k is valid/defined.
p (i.e. o[k]) is not null or undefined (i.e. is an Object)
p.properties does not evaluate to false, if you are testing for existence
p.items is not null or undefined (i.e. is an Object)
p.items.properties does not evaluate to false, if you are testing for existence
A more robust implementation would be:
if (typeof o === 'object' && o !== null) {
var p;
if (o.hasOwnProperty('title')) {
p = o;
} else {
p = o[k];
}
if (typeof p === 'object' && p !== null) {
if (p.hasOwnProperty('properties')) {
p.properties = newData;
} else if (typeof p.items === 'object' && p.items !== null
&& p.items.hasOwnProperty('properties')) {
p.items.properties = newData;
}
}
}
This still relies on:
k is valid/defined.
Basically, it is OK to use shortcuts like if(o.title) to test for existence, if you know that
the possible values for o can not include ones which might make your code throw an error (e.g o is null or undefined), and
the possible values for o.title do not evaluate to false when the property actually exists (e.g. o.title is null, undefined (yes, the property can exist, but have the value undefined), false, 0, '', etc.).
If you are going to perform the replacements in other areas of your code, or if you are going to use property keys other than hard coded items, and properties, then you should create a function. Assuming you are only performing this replacement in this section of your code, using a variable to hold the object in which you are looking for properties is faster/more efficient than creating a function.
Ok, from what i can understand here, it's like you are trying to replace the "properties" with the new data, and you want this to be able to be done dynamically, or maybe i can say, you need to do this regardless the structure.
lets see, if your objective is anything that end up with "properties", lets do it like this:
function recReplace(current,target,replacement){
for (var i in current){
if (i == target){
current[i] = replacement;
}
else{
recReplace(current[i],target,replacement);
}
}
}
And in the end you call
recReplace(o,"properties",newData);
But this will replace whole "properties" key with newData in DFS way, you can do additional conditional if you want to replace it only the first occurence

Categories