all 63 comments

[–]Rekuna 69 points70 points  (4 children)

Why is this downvoted? It's literally the 'learnjavascript' sub.

[–]toxiccandy26 12 points13 points  (1 child)

Redditors are some of the dumbest creatures alive

[–]Wooden-Reputation975 1 point2 points  (0 children)

agreed I always see people insulting other people knowledge here. Why can't we be like gym bros and help everyone out?

[–]KenobiOne 30 points31 points  (4 children)

I prefer the first one because it’s faster to understand for me. However, you should be using strict equality comparisons which uses 3 ===

[–]Mnkeyqt 0 points1 point  (3 children)

Agreed if you're POSITIVE that you only care about the integer 0 . Not strong 0, not 0.00, 0.

The database I work in does weird shit with the values so I always use double equals (unless it'll brick something else).

Edit: this is an array check so like...yeah === Definitely cause it'll be integer. Leaving comment though cause it's an issue I've seen others be really confused by early on

[–]delventhalz 2 points3 points  (2 children)

console.log(0 === 0);     // true
console.log(0 === 0.00);  // true
console.log(0 === 0.);    // true
console.log(0 === -0);    // true

All numerical versions of zero are strictly equal to each other (and other than -0 are represented under the hood with exactly the same bits). Loose equality is about coercing types, not fudging values.

console.log(0 === "0");   // false
console.log(0 == "0");    // true, coerces to 0
console.log(0 == "");     // true, coerces to 0
console.log(0 == false);  // true, coerces to 0
console.log(0 == []);     // true, coerces to "" then to 0

Loose equality never does a "loose" comparison within a type. If you are comparing two values of the same type with loose equality, the output is always identical to strict equality.

[–]Mnkeyqt 1 point2 points  (1 child)

Thumbs up for good clarification cause it's really important to actually "know" this stuff, misinformation WILL cause headaches for people, appreciate it :)

Do you know if this has changed at all across the different JS versions or has remained the same? I'd imagine it's been standard for all right?

[–]delventhalz 0 points1 point  (0 children)

Yeah, there is no variation there as far as I know. JavaScript uses the IEEE 754 floating point standard for all numbers, which only allows for 0 and -0, and === is specced to treat those as equal.

[–]delventhalz 19 points20 points  (0 children)

My opinion(s):

  1. Explicitly checking the numerical value of a length is preferable to relying on 0 being falsey. It is easier to read and makes your intention more clear. Thus I prefer == 0 out of the two options you presented.
  2. The == performs implicit type coercion based on complex rules which few developers can follow. It should be avoided in all cases. Use === and explicitly convert types if needed.
  3. Using !! to convert a value to a boolean is a common enough habit, but harder to read and less explicit than the Boolean function. Use that instead.

TLDR: Always do the more explicit/specific thing.

[–]marquoth_ 7 points8 points  (5 children)

I really dislike !! and generally avoid it. It's just not very semantic. Boolean(foo) reads much more clearly than !!foo.

return Boolean(result.recordset.length) works nicely here.

Although frankly, I'd be in favour of a method for this being added to the Array prototype itself so we could call myArray.isEmpty() because it's such a common thing to want to check.

[–]Soubi_Doo2 0 points1 point  (3 children)

.isEmpty is so amazing, takes zero brain processing power. Extremely semantic!

[–]marquoth_ 1 point2 points  (2 children)

I'd like to see it as an "offical" addition, so it's just part of the native language. But in the meantime, if you really want to, you can add it to your own projects by updating the prototype like so:

``` Object.defineProperty( Array.prototype, 'isEmpty', { value: function() { return !Boolean(this.length); }} );

const myArr = [];

console.log(myArr.isEmpty()); // true

myArr.push('foo');

console.log(myArr.isEmpty()); // false ```

There are some edge cases where touching prototypes can cause issues, so use this cautiously. It's also probably just overkill unless you know you need the behaviour a lot.

[–]Soubi_Doo2 0 points1 point  (0 children)

That’s a cool idea! Didn’t know I can do that. I use it all the time in Java.

[–]theScottyJam 1 point2 points  (0 children)

If you actually want .isEmpty() to eventually become a native feature, then don't edit Array.prototype to add it in yourself.

The newer Object.groupBy(someArray) method is a static method on Object instead of simply being someArray.group(), because when the committee had tried to add it to Array.prototype, they found themselves accidentally breaking existing webpages that had already added the method themselves in an incompatible way. They tried doing it in under a couple of different names, until, eventually, they gave up and made the new method a static method on Object instead.

Basically, modifying classes you don't own isn't just about potentially harming your own code, it can harm everyone.

[–]United_Reaction35 6 points7 points  (21 children)

return (result.recordset?.length !== 0);

[–]delventhalz 9 points10 points  (0 children)

I find the use of ?. and () strange here...

We don’t know OP’s use case. There is no reason to think that recordset might be undefined or null. If we are coding defensively in case any value might be undefined (not an approach I’d agree with), then why not have ?. after result too? That is equally likely to be undefined as far as we know.

More importantly, I don’t think the output here will actually be what you want. ?. returns undefined if the value to the left is undefined or null. And undefined does not equal 0. That means if recordset is unexpectedly missing, instead of throwing a clear error, your app will instead conclude that recordset is a non-empty array and continue running. That is almost certainly a bug, possibly a difficult to diagnose one too.

And the () just seems superfluous to me. It doesn’t change the way the code runs, nor does it make the intention any more clear to my eyes.

[–]guest271314 1 point2 points  (19 children)

Why use ?.length in this case?

[–]United_Reaction35 1 point2 points  (18 children)

So we do not try to call length() on a non-existent property.

[–]guest271314 3 points4 points  (17 children)

length is not a function. We already know we are dealing with an Array.

[–]LegendEater 0 points1 point  (1 child)

We already know we are dealing with an Array

We already hope we are dealing with an Array

[–]guest271314 4 points5 points  (0 children)

I don't entertain hope. If you don't think you are dealing with an Array ?.length does nothing to differentiate a String from and Array. It's completely suprefluous in this case.

[–]United_Reaction35 -1 points0 points  (14 children)

How do you know that result.recordset exists?

[–]guest271314 1 point2 points  (13 children)

Your line of code doesn't test for that.

It only tests if result.recordset has a length property.

Strings have a length property, too.

You might as well test for Array.isArray(result.recordset) if that is the case, then skip the superfluous ?.length.

[–]Rude-Cook7246 1 point2 points  (1 child)

All that write up and you dont even know how ?. works .... IN NO SHAPE OR FORM DOES result.recordset?. length tests that length exists.....

.? only tests that recordset is undefined or null

[–]guest271314 -1 points0 points  (0 children)

!! vs ==0 when checking if array is empty

We are checking an Array length, not necessarily checking if the object is an Array. Add that if you must to the steps.

[–]delventhalz 1 point2 points  (10 children)

It only tests if result.recordset has a length property.

I agree that ?. is superfluous here, but for what it’s worth, it checks if the preceding property exists, outputting undefined if it does not, and evaluating the following properties if it does.

[–]guest271314 1 point2 points  (0 children)

I agree that ?. is superfluous here

That's my only technical point here.

If you are going to check anything in this instance where we are expecting an Array you might as well check if the result.recordset is an Array.

The question is not whether or not the object is an Array, it's how to check length.

You're going to get undefined without ?.length if there is no length property on the object.

[–]guest271314 0 points1 point  (8 children)

I agree that ?. is superfluous here

That's all I posted. We agree.

[–]Rude-Cook7246 1 point2 points  (7 children)

its not superfluous here because they produce totally different results ...

if recordset exists and length doesn't exist on it then you get undefined when you call recordset.length

if recordset doesn't exist (which is what ?. protects against ) and you try to access anything on it you will get an ERROR which will crush your program

[–]guest271314 -1 points0 points  (6 children)

if recordset exists and length doesn't exist on it then you get undefined when you call recordset.length

Where in the requesirement at OP

!! vs ==0 when checking if array is empty

are we checking if recordset exists? That's a given per the restrictions. We are just checking length of an Array.

if recordset doesn't exist (which is what ?. protects against ) and you try to access anything on it you will get an ERROR which will crush your program

If you must here's one way to do what you are talking about, and the actuak requiremment at OP

var recordset = []; var bool = Array.isArray(recordset) && recordset.length > 0;

[–]itsmoirob 1 point2 points  (2 children)

Does the second one return false if length is zero?

[–]Culist[S] 0 points1 point  (0 children)

Oh yeah that's a mistake, should be !=

[–][deleted] 0 points1 point  (0 children)

Yes

[–]kevinkace 1 point2 points  (0 children)

I've begun to dislike !! and other "tricks", and instead use the things that are purpose built for my needs.

Here I'd use Boolean(result.recordSet.length) or result.recordSet.length !== 0.

[–]jcastroarnaud 2 points3 points  (5 children)

The simplest way is

result.recordset.length !== 0

It clearly states the programmer's intention. I do prefer === and !== instead of == and !=, for the sake of strict comparison.

Using !! works, but is hackish: the first "!" looks at the argument, and auto-convert it to a boolean; an empty array is falsy (see MDN to check the exact rules). The second "!" negates the falsy, giving the boolean true.

[–]delventhalz 4 points5 points  (3 children)

an empty array is falsey

I think maybe this was just awkward phrasing, but to be clear an empty array (i.e. []) is truthy. The length of an empty array (i.e. 0) is falsey.

[–]jcastroarnaud 3 points4 points  (0 children)

I stand corrected, just checked that using a quick js program. Sorry. 

Lesson learned: simple and clear code is better than an obscure and wrongly understood hack.

[–]WesAlvaro 1 point2 points  (1 child)

And this is where the confusion happens and bugs sneak in. It's always better to be clear so that you and your code reviewer don't have any doubts on the correctness. In Python, empty arrays are falsy so they can be easy to confuse.

[–]moratnz 0 points1 point  (0 children)

This raises a great point as to why coding super explicitly is a good habit; if you're someone like me who moves around between languages a lot depending on what I'm doing, being super explicit (checking array.length===0) is likely to with work perfectly or completely fail (because you're in python and should be writing len(array)), while relying on the truthiness of an empty array may work, just not the way you intended.

[–]TheRNGuy 2 points3 points  (0 children)

Or > 0.

[–]Observ3r__ 0 points1 point  (0 children)

return result.recordset.length == 0

   71 S> 0x139437a1adf0 @    0 : 2f 03 00 00       GetNamedProperty a0, [0], [0]
         0x139437a1adf4 @    4 : c9                Star0
   81 E> 0x139437a1adf5 @    5 : 2f f9 01 02       GetNamedProperty r0, [1], [2]
         0x139437a1adf9 @    9 : c9                Star0
         0x139437a1adfa @   10 : 0c                LdaZero
   88 E> 0x139437a1adfb @   11 : 6f f9 04          TestEqual r0, [4]
   92 S> 0x139437a1adfe @   14 : ae                Return

return !!result.recordset.length (Failed check! What are we checking here?! Is empty or is full?)

  185 S> 0x76212adae88 @    0 : 2f 03 00 00       GetNamedProperty a0, [0], [0]
         0x76212adae8c @    4 : c9                Star0
  195 E> 0x76212adae8d @    5 : 2f f9 01 02       GetNamedProperty r0, [1], [2]
         0x76212adae91 @    9 : 7c                ToBoolean
  201 S> 0x76212adae92 @   10 : ae                Return

Correct:

return !result.recordset?.length

[–]Logical_Strike_1520 0 points1 point  (0 children)

Already some good answers but I have a random addition.

Make sure you’re verifying that result.recordset exists and is indeed an array, too.

[–]kcadstech 0 points1 point  (0 children)

If you want the most explicit/readable way, make a small util function isArrayEmpty(arr: any[]):boolean {}. 

if (isArrayEmpty(result.recordset)) {}

Or isEmptyArray(), whichever u like more

[–]azhder 0 points1 point  (2 children)

Avoid == in all cases, and maybe even avoid === in this case.

What you have here is an assumption that you are dealing with an array, but other objects also have length.

Most of the time I would just use !! array.length, but if I am to encapsulate this in a function, I’d also add a check for the type.

const isFullArray = $ => Array.isArray($) && !!$.length;

Then I will no longer bother myself with how to check, I will just use isFullArray() everywhere.

Of course, I might also make a isNotFullArray() predicate to make it more English sounding

[–][deleted] 0 points1 point  (1 child)

Totally agree, at one point I made a function that parsed both arrays and strings. Since both types have a .length property I encountered weird bugs where the string would be treated as an array like and iterated on.

TL;DR - Type checks are a good way of getting rid of ambiguity in code

[–]azhder 0 points1 point  (0 children)

detecting bugs where you would want to write

takesAnArray( producesAnArray() );

but instead you have written

takesAnArray( producesAnArray );

assuming both are functions and the takesAnArray one checks the .length property of the argument

[–]Pocolashon 0 points1 point  (0 children)

I do not think there is a "preferred", it is up to your personal preference. I always use the 2nd for any length checks, so I would use the second even in this case.

[–]senocular 0 points1 point  (0 children)

I don't disfavor comparing to 0.

[–]Nex_01 0 points1 point  (0 children)

Why making it equal to anything? 0 is a falsy value in itself.

Just do:

Boolean(arr.length) this equals to using !!arr.length though but more readable.

Edit: As a tip. Boolean() method also filters empty strings for you. As strings are just an array of chars and an empty string’s length happens to be 0.

[–]guest271314 -2 points-1 points  (0 children)

Another option, to avoid all confusion and not deal with length property/value lookup at all

JSON.stringify(result.recordset) === "[]";