Loading...

Follow First Class JS | Modern Javascript from A to Z on Feedspot

Continue with Google
Continue with Facebook
or

Valid

Unfortunately, there is no build-in isEmpty or similar method to checking if specific object is empty(has own properties) in JavaScript. We usually create our own helper method or use an external library like lodash. Although, these libraries are offering a lot of goodies, it is highly impractical to import them just for using one or two methods(like buying a Ferrari to drive to the supermarket).

My favorite way to check if a specific object is empty is using the Object.getOwnPropertyNames method.This method returns an array with all own properties of a specific object.

We can use this functionality to achieve our goal like this:

const obj = {};
Object.getOwnPropertyNames(obj).length === 0;
// [].length === 0  => true
const obj = { a: 2};
Object.getOwnPropertyNames(obj).length === 0;
// ["a"].length === 0 => false 

Or wrap it in a so called “helper” function, which will be used whenever needed.

function isEmpty(obj) {
  return Object.getOwnPropertyNames(obj).length === 0;
}

You can use this method in browsers that support ES5+

The other popular way to check if a specific object is empty, but is also compatible with older versions of the popular browsers is using for..in (check for compatibility info here)

function isEmpty(obj) {
    for(var prop in obj) {
        if(obj.hasOwnProperty(prop)) return false;
    }
    return true;
}

isEmpty({}); // true
isEmpty({a:5}); // false

Theoretically, this will result in a better performance than the previous method. When looping thru the object, it will return false right after the first own property, whereas the first one extracts all the properties and then check the length.

Both of the ways above works with empty objects – {} and objects containing own properties { a: 5 }. However, if you try to use this methods with different type of input, like “string”, both of them will result that this “string” is not empty.

If your intention is your helper method to work with different input types(not only objects), you will want to extend your logic. I would highly suggest you to take a look at the implementation of the popular lodash library – https://github.com/lodash/lodash/blob/master/isEmpty.js

As a library used by a million of developers, you can be sure that a lot edge cases are handled there and you don’t have to reinvent the wheel.

Conclusion

If you know that your input to the helper function will be always object(empty or not), then you can confidently go with any of the first two methods. In case you want your function to be more flexible and of course expect a different types as input parameters, explore the provided by lodash and adapt it to your use case.

The post Check if Object is empty in Javascript appeared first on First Class JS.

Read Full Article
  • Show original
  • .
  • Share
  • .
  • Favorite
  • .
  • Email
  • .
  • Add Tags 

Here are two truths about JavaScript:

  • JavaScript does’t have integers, so you have to use numbers instead.
  • JavaScript arrays are not typed, you can put whatever you want in them.

Sounds reasonable? Well while these are true, and JavaScript’s semantics will honour these, the truth gets a bit more fuzzy as you delve into the details of what happens behind the scenes in the JavaScript engine.

The Google V8 engine is used in the Chrome browser and Node programming environment. This engine will make optimisations based on the runtime behaviour of your program. This means the values in your numbers and arrays actually count for something when it comes to peformace.

V8 knows roughly what kinds of items are in your arrays, and it uses this information to make performance optimisations. It marks arrays stored in memory as a certain ‘type’, because if, for example it knows the array only contains integers (whole numbers), it can work with them more efficiently.

There are 6 types of array that V8 knows about, which are represented in this diagram taken from v8.dev:

To make sense of this diagram, you can think of it as 3 types of elements:

  • SMI_ELEMENTS – The array contains only integers, e.g. [1, 2, 3]
  • DOUBLE_ELEMENTS – The array only contains double precision floating point numbers, e.g. [1, 2.1]
  • ELEMENTS – The array could contain anything, e.g. [1, 3, 'x']

And these type of elements are accompanied by the words Holey and Packed. Those mean:

  • HOLEY – The array may have holes. I will explain how this can happen shortly.
  • PACKED – Every element of the array has a value (or undefined), e.g. ['x','y',undefined,'z']

The transition arrows shown in the diagram are one way transitions that can happen to the type of an arrays as elements are removed, inserted or updated.

You can go from integer to double (but not back). You can go from double to anything (but not back). You can go from packed to holey (but not back).

Keep in mind that the type of array is just about V8’s knowledge of what the array might contain, which it uses for performance optimisation. It doesn’t affect the actual behaviour of your program. For example if you add a string value 'X' to a PACKED_SMI_ELEMENTS array, that is OK to do, but it means the V8 Engine will now treate it as though it can contain anything, even if you remove the 'X' later on.

Holey Arrays

A holey array is one where there are values that are not defined. This doesn’t mean that the value is the JavaScript value of undefined, but rather they haven’t been set yet. For this to happen you need to leave a gap when inserting elements, e.g.:

var holey = [1, 2, 3];
holey[9] = 9;
// holey has holes at positions 3, 4, 5, 6, 7 and 8.
Performance

For best performance, you want the type of array to be as far top and left in the diagram as possible. This means you wan’t to avoid holey arrays, and keep your arrays as SMI if possible, otherwise double if possible.

Avoiding holey arrays should be easy enough in most cases, and follows from idiomatic (i.e. common case) JS programming, such as using the map or push functions, which don’t leave holes. Be careful when inserting data by index into an array.

Keeping to SMI or Double arrays is only really applicable if you really are dealing with those types in the first place.

The trick is not to add other types to those arrays like strings or objects. If you are used to mixing types in array for whatever reason then it might be worth thinking about changing that habit if it impacts peformace.

And remember if your application is performaing reasonably it might be best to ignore these optimisations so that you have one less thing to worry about. Use them when you really need to make something go faster because it is impacting the users of your application.

Credits

I learned this stuff attending a talk by Mathias Bynens live in Melbourne JSConf in 2018. Luckily for you (and me!) the talk is available on https://v8.dev/blog/elements-kinds along with an article going into more depth than I have here. Fascinating stuff.

Summary

This article is a quick overview of arrays and performance in the V8 engine. There is more depth to this on the v8.dev website. If you want to know more about arrays, see JavaScript arrays or my website about JavaScript.

Martin Capodici

The post Under The Hood: Arrays in JS appeared first on First Class JS.

Read Full Article
  • Show original
  • .
  • Share
  • .
  • Favorite
  • .
  • Email
  • .
  • Add Tags 

The purpose of this article is to share with you the best ways to remove duplicate objects from JavaScript Array based on specific property/key. We will also analyze their performance in terms of execution time for different lengths of the array.

If you want to learn how to remove duplicate PRIMITIVE values from JavaScript array check this article.

Here are the ways we will explore one by one:

  • Filter
  • Filter + Set
  • Filter + FindIndex
  • Reduce + Find
  • For
  • ForEach

Before continue with implementing them, I want to share the data I will be using for testing. I’m sharing it here, so I can omit it later in the article for better visualisation purposes.

const employees = [
    { id: 1, name: 'John Smith' },
    { id: 2, name: 'John Smith' },
    { id: 3, name: 'John Smith' },
    { id: 4, name: 'John Smith' },
    { id: 2, name: 'John Smith' },
    { id: 2, name: 'John Smith' },
    { id: 9, name: 'John Smith' },
    { id: 6, name: 'John Smith' },
    { id: 1, name: 'John Smith' },
    { id: 4, name: 'John Smith' },
];
Filter
function removeDuplicates(array, key) {
    let lookup = {};
    return array.filter(obj => !lookup[obj[key]] && lookup[obj[key]] = true);
}
console.log(removeDuplicates(employees, 'id'))
// [{ id: 1, name: 'John Smith' },{ id: 2, name: 'John Smith' },{ id: 3, name: 'John Smith' },{ id: 4, name: 'John Smith' },{ id: 9, name: 'John Smith' },{ id: 6, name: 'John Smith' } ]

One of my favorite methods. We are taking advantage of the .filter method provided by the Array.prototype. The method is returning a new array with all the elements that pass a specific test(written by us).

The logic for removing is extracted to a function that accept two arguments. The first one is the array we want to remove the duplicate objects and second one – the key we want to use for comparing them.

In our case we are calling it with the employees array and pass ‘id’ as a key. This is our unique object identifier.

Declaring a variable ‘lookup’, initially it will be an empty object, but we will use that object to collect the already added key values. What the callback function of the .filter is doing is to check if this key value already exist as a property in the lookup object.

If such a value already exists, then this object has already been added to the new unique array and we are skipping it.
In case it doesn’t exist, we are adding it to the lookup and set it to true.

This way, when the next object with the same value of the key is added, we will just skip it by returning false from the callback function,i.e. !lookup[obj[key]] will be false.

Filter + Set
function removeDuplicates(array, key) {
    let lookup = new Set();

    return array.filter(obj => !lookup.has(obj[key]) && lookup.add(obj[key]));
}
console.log(removeDuplicates(employees, 'id'))
// [{ id: 1, name: 'John Smith' },{ id: 2, name: 'John Smith' },{ id: 3, name: 'John Smith' },{ id: 4, name: 'John Smith' },{ id: 9, name: 'John Smith' },{ id: 6, name: 'John Smith' } ]

This method is the same as the previous one with single difference – we are using the Set data structure instead of simple object. I’m curious about the performance differences between these two data structures, but we will check the results in the comparison section.

Filter + FindIndex
function removeDuplicates(array, key) {
    return array.filter((obj, index, self) =>
        index === self.findIndex((el) => (
            el[key] === obj[key]
        ))
    )
}
console.log(removeDuplicates(employees, 'id'))
// [{ id: 1, name: 'John Smith' },{ id: 2, name: 'John Smith' },{ id: 3, name: 'John Smith' },{ id: 4, name: 'John Smith' },{ id: 9, name: 'John Smith' },{ id: 6, name: 'John Smith' } ]

A little bit slower method. What we basically do is using the good old filter method, but instead of saving the already added key values to a lookup object, we are using findIndex to find the index of the current object. Taking advantage of the fact that findIndex always returns the index of the first found object, we are comparing it with the current object index.

If they are the same, this means that the object is still not added to the new unique array. If the index is different, the comparison will return false and the object will be skipped(not added again).

Reduce + Find
function removeDuplicates(array, key) {
    return array.reduce((accumulator, element) => {
        if (!accumulator.find(el => el[key] === element[key])) {
          accumulator.push(element);
        }
        return accumulator;
      }, []);
}
console.log(removeDuplicates(employees, 'id'))
// [{ id: 1, name: 'John Smith' },{ id: 2, name: 'John Smith' },{ id: 3, name: 'John Smith' },{ id: 4, name: 'John Smith' },{ id: 9, name: 'John Smith' },{ id: 6, name: 'John Smith' } ]

Reduce is another method provided to us by the Array prototype. The “reduced” value is stored in the so called accumulator, which is returned at the end. In our case the accumulator will contain our unique array.

For every iteration, we are doing a check. Is the accumulator already containing an object with property(key) value, equal to the current object property value. If there is no such an object, we are pushing the current one to the accumulator. Otherwise, just returning the current accumulator, without pushing a new one.

For
function removeDuplicates(array, key) {
    let lookup = {};
    let result = [];

    for(let i=0; i<array.length; i++) {
        if(!lookup[array[i][key]]){
            lookup[array[i][key]] = true;
            result.push(array[i]);
        }
    }

    return result;
}
console.log(removeDuplicates(employees, 'id'))
// [{ id: 1, name: 'John Smith' },{ id: 2, name: 'John Smith' },{ id: 3, name: 'John Smith' },{ id: 4, name: 'John Smith' },{ id: 9, name: 'John Smith' },{ id: 6, name: 'John Smith' } ]

Like in the first two methods, we are using a helper variable which is helping as to keep track of the already added objects. The only difference is that instead of using the .filter method, we are using the most basic for loop. I suspect that the performance will be better comparing to the filter, due to the fact that we are not executing callback function for every element. Will see it in a minute.

ForEach
function removeDuplicates(array, key) {
    let lookup = {};
    let result = [];

    array.forEach(element => {
        if(!lookup[element[key]]) {
            lookup[element[key]] = true;
            result.push(element);
        }
    });

    return result;
}
console.log(removeDuplicates(employees, 'id'))
// [{ id: 1, name: 'John Smith' },{ id: 2, name: 'John Smith' },{ id: 3, name: 'John Smith' },{ id: 4, name: 'John Smith' },{ id: 9, name: 'John Smith' },{ id: 6, name: 'John Smith' } ]

Like the previous one, but instead of the basic for, using the .forEach. Like I mentioned, it could be a little bit slower due to the fact that forEach if using the callback approach, i.e. calling a function for every element.

Here is the promised performance comparison

I’m comparing the execution time for all the methods above for different array lengths – 10, 1 000, 10 000, 100 000 and 1 000 000.

All the tests below are executed in Google Chrome, with the same input data. The results are in milliseconds.

Conclusion

We can conclude from the performance charts, that the Filter, Filter + Set, For and Foreach are having really close performance results and you can’t go wrong with them. However, the ‘nested’ solutions – Filter + FindIndex and Reduce + Find are good for small arrays, but when the things become serious, they are too slow and can even freeze the browser/environment. This is the reason for excluding them from charts in 10 000 entries and more.

The post Remove duplicate objects from JavaScript array – How to & Performance comparison appeared first on First Class JS.

Read Full Article
  • Show original
  • .
  • Share
  • .
  • Favorite
  • .
  • Email
  • .
  • Add Tags 

And here we are, it’s that time of the year when a new version of ECMAScript is released. What does it actually means ? There is a defined process before a feature can be approved by TC39 committee and go live – be included in the specification.

Every feature goes through the following process:

Stage 0: Ideas/Strawman

Stage 1: Proposals

Stage 2: Drafts

Stage 3: Candidates

Stage 4: Approved

According to this tweet from Mathias Bynens(member of the TC39 committee), the features that made it to Stage 4 the last year and will be included into the specification are:

  1. Array#{flat,flatMap}
  2. Object.fromEntries
  3. String#{trimStart,trimEnd}
  4. Symbol#description
  5. try { } catch {} // optional binding
  6. JSON ⊂ ECMAScript
  7. Well-formed JSON.stringify
  8. Stable Array#sort
  9. Revised Function#toString

We will explore them one by one.

Array#{flat,flatMap}

These two methods are now available in the Array prototype.

flat()

The name “flat” is pretty much self explanatory. This method returns a flattened version of the array it was called on.

Here is an example basic usage:

const arr = [1,2,[1,2],[2,3]];
const flatArr = arr.flat();
console.log(flatArr); // [1,2,1,2,2,3]

The flat method also supports optional argument specifying the depth of flattening. In case of no argument pass, the default value will be 1(depth of 1).

If we call the flat method on an array with level of nesting more than 1, it will flatten only the first level.

Example:

const arr = [1,2,[1,2,[4,5]],[2,3]];
const flatArr = arr.flat();
console.log(flatArr); // [1, 2, 1, 2, [4 5], 2, 3]

The array [4,5] doesn’t get flattened.

If we pass an argument “2” to the method, it will be flattened correctly:

const arr = [1,2,[1,2,[4,5]],[2,3]];
const flatArr = arr.flat(2);
console.log(flatArr); // [1, 2, 1, 2, 4, 5, 2, 3]

What about if we don’t know the depth of the array ? Then, we can use the “Infinity” as an argument. Like this:

const arr = [1,2,[1,2,[4,5,[6,7]]],[2,3]];
const flatArr = arr.flat(Infinity);
console.log(flatArr); // [1, 2, 1, 2, 4, 5, 6, 7, 2, 3]
flatMap()

This method has the same effect/functionality as using the map() and then flat(), i.e. it maps the values to a new ones and then flatten the result. The other difference here is that no argument for depth as the one in flat supported. It’s using depth of 1 as default.

Here is an example and comparison between flatMap and map:

const names = ["John", "Johny", "Peter"];

const mapOnly = names.map((name, idx) => [idx, name]);
const flatMap = names.flatMap((name, idx) => [idx, name]);

console.log(mapOnly); // [[0, “John”],[1, “Johny”],[2, “Peter”]]
console.log(flatMap); // [0, “John”,1, “Johny”,2, “Peter”]

The callback function structure expected by flatMap is the same as map.

Object.fromEntries

This method is the opposite of Object.entries()

While the Object.entries() transform object entries to an array of [key, value] pairs, the Object.fromEntries() transform a list of [key, value] pairs into an object.

It works on both maps and simple nested arrays:

const map = new Map().set("id", 1).set("name", "John");
const arr = [["id", 1], ["name", "John"]];

const mapObj  = Object.fromEntries(map);
const arrObj = Object.fromEntries(arr);

console.log(mapObj); // {id: 1, name: ‘John’}
console.log(arrObj); // {id: 1, name: ‘John’}
String#{trimStart,trimEnd}

Many of the browsers already support trimRight and trimLeft, but in order to be consistent with the padStart and padLeft, a decision to create trimStart and trimEnd was made.

Both methods are attached to the prototype of String.

An example how to use them:

const exampleStr = "      first class js     ";

const trimStart = exampleStr.trimStart();
const trimEnd = exampleStr.trimEnd();

console.log(trimStart); // "first class js     "
console.log(trimEnd);   // "      first class js"
Symbol#description

A new read-only property was added to the Symbol objects. It’s optional and is returning the description of the Symbol.

An example:

const mySymbol = Symbol(“first”);

const description = mySymbol.description;

console.log(description); // “first”
try { } catch {} // optional binding

Optional catch binding was also introduced.

Before this proposal, we were forced to bind exception variable for the catch clause, like this:

try {
 // do something
} catch(err) {
 // throw custom error, i.e. don’t use the err variable
}

From now on, we are able to omit the exception variable and do the following:

try {
 // do something
} catch {
 // throw custom error
}
JSON ⊂ ECMAScript

The line separator (U+2028) and paragraph separator (U+2029) symbols were not allowed in string literal.

Previously, these were treated as line terminators and were causing SyntaxError. This solution extends the ES strings, so the mentioned above symbols be accepted.

// Produces invalid string before ES2019
eval('"\u2028"');
eval('"\u2029”’);
// Valid in ES2019
eval('"\u2028"');
eval('"\u2029”’);
Well-formed JSON.stringify

Before ES2019, the calling ofJSON.stringify() with surrogate UTF-8 code(U+D800 to U+DFFF) was returning a single UTF-16 code unit, while now it represent them as strings. This way they can be transformed back to their original representation with JSON.parse();

// Before ES2019
JSON.stringify('\uD800'); // ""
// Now 
JSON.stringify('\uD800'); // "\ud800"
Stable Array#sort

According to this tweet from Mathias Bynens, the sort method for arrays is now stable. It’s using the stable TimSort algorithm for arrays over 10 elements instead of the unstable QuickSort.

Array.prototype.sort is now stable in @v8js v7.0 / Chrome 70!

Previously, V8 used an unstable QuickSort for arrays with more than 10 elements. Now, we use the stable TimSort algorithm.

Mathias Bynens

You can check the tweet for a demo.

Revised Function#toString

The available toString method in the functions prototype is nothing new. It just return a string representing the function code. But what is the difference now ?

Previously(before ES2019) this method was returning the code with some modifications like stripping the comments, removing some whitespaces and etc.

Now, the code is returned as it was declared – with all the whitespaces, comments and formating.

Before:
function /* this is comment */ testFunc() {}

console.log(testFunc.toString()); // function testFunc() {}

Now:
function /* this is comment */ testFunc() {}

console.log(testFunc.toString()); // function /* this is comment */ testFunc() {}
Conclusion

Comparing to ES2015, the new version of ECMAScript doesn’t introduce so many new changes. However, we can agree that all of them are really useful and will take their place in the future of the Javascript development.

If you are curious what we can expect from the community or just want to be up to date with the latest features, you can check the TC39 proposals, available publicly on GitHub.

The post ECMAScript 2019(ES2019) – New Features with Examples appeared first on First Class JS.

Read Full Article
  • Show original
  • .
  • Share
  • .
  • Favorite
  • .
  • Email
  • .
  • Add Tags 

Separate tags by commas
To access this feature, please upgrade your account.
Start your free month
Free Preview