Programming Series with Array and Object Data in Javascript - Handling Plain Data

Programming Series with Array and Object Data in Javascript - Handling Plain Data

Daily short news for you
  • Privacy Guides is a non-profit project aimed at providing users with insights into privacy rights, while also recommending best practices or tools to help reclaim privacy in the world of the Internet.

    There are many great articles here, and I will take the example of three concepts that are often confused or misrepresented: Privacy, Security, and Anonymity. While many people who oppose privacy argue that a person does not need privacy if they have 'nothing to hide.' 'This is a dangerous misconception, as it creates the impression that those who demand privacy must be deviant, criminal, or wrongdoers.' - Why Privacy Matters.

    » Read more
  • There is a wonderful place to learn, or if you're stuck in the thought that there's nothing left to learn, then the comments over at Hacker News are just for you.

    Y Combinator - the company behind Hacker News focuses on venture capital investments for startups in Silicon Valley, so it’s no surprise that there are many brilliant minds commenting here. But their casual discussions provide us with keywords that can open up many new insights.

    Don't believe it? Just scroll a bit, click on a post that matches your interests, check out the comments, and don’t forget to grab a cup of coffee next to you ☕️

    » Read more
  • Just got played by my buddy Turso. The server suddenly crashed, and checking the logs revealed a lot of errors:

    Operation was blocked LibsqlError: PROXY_ERROR: error executing a request on the primary

    Suspicious, I went to the Turso admin panel and saw the statistics showing that I had executed over 500 million write commands!? At that moment, I was like, "What the heck? Am I being DDoSed? But there's no way I could have written 500 million."

    Turso offers users free monthly limits of 1 billion read requests and 25 million write requests, yet I had written over 500 million. Does that seem unreasonable to everyone? 😆. But the server was down, and should I really spend money to get it back online? Roughly calculating, 500M would cost about $500.

    After that, I went to the Discord channel seeking help, and very quickly someone came in to assist me, and just a few minutes later they informed me that the error was on their side and had restored the service for me. Truly, in the midst of misfortune, there’s good fortune; what I love most about this service is the quick support like this 🙏

    » Read more

Problem

In any programming language, whether it's back-end or front-end, data processing is very common. It can involve tasks such as preprocessing before displaying it on the user interface, or smoothing the data and returning it to the client through an API, for example.

Array and Object are two objects that we need to process the most. In this series, I will present to readers some experience in data processing with these two objects.

The powerful trio: map, filter, and reduce

It must be said that the three functions map, filter, and reduce are very helpful. If you still have a vague idea about them, you should take the time to learn about them. I will summarize a little about the usefulness of these three functions.

map function

The map function works with data types being an Array. It takes a function as input and returns an array with the length of the elements always equal to the original data. In essence, we use map when we want to iterate over the elements in the array to add/modify/delete... their data.

The callback of map has the element, index, and array parameters corresponding to the current element, position, and data of the array.

map(function callbackFn(element, index, array) { ... }, thisArg)

map is an implementation of a Functor, if you don't know what a functor is, you can read my article titled What is a Functor? Do I need to know about Functors?.

For example, an array of users with basic information is as follows:

const users = [
  {
    id: 1,  
    name: "Nguyễn Văn A",  
    age: 18,  
    status: "active",  
    city_code: "HN"
  },  
  {
    id: 2,  
    name: "Trần Thị B",  
    age: 20,  
    status: "active",  
    city_code: "HCM"
  },  
  {
    id: 3,  
    name: "Phạm Thị Xuân C",  
    age: 26,  
    status: "block",  
    city_code: "HN"
  },  
]

Increase the age of each person by 1:

const usersIncAge = users.map(item => {
  ...item,  
  age: item.age + 1;
});

Note: In this case, I deliberately create a new object and return it to avoid reference, so after running, there is an array usersIncAge that does not reference users at all.

I have seen people often write:

users.map(item => item.age += 1);

It is much shorter, but unintentionally modifies the data of users, which can lead to data discrepancies if you cannot manage the use of users in other places. So if possible, you should avoid this writing style, instead, create a new array. If you are interested in this issue, read the article Pure Function in Javascript. Why should we know it as soon as possible?.

filter function

filter is used to filter out elements that satisfy the condition in the array. The output of filter is always an array with a length less than or equal to the original data.

The callback function of filter is similar to map, it contains the parameters element, index, and array.

For example, I want to filter out a list of users with age >= 20:

users.filter(item => item.age >= 20);

Filter will use the result of the callback function returning true or false to filter the data, if true is taken, and if false is not. Since filter can only filter elements from the original array, the newly created data from filter will have a reference to the original data, so be careful.

reduce function

Unlike map and filter, the output of reduce is not guaranteed. It can be any data type depending on the purpose.

Reduce will iterate over each element in the array, perform a calculation function, and return a single result.

The callback function of reduce has 4 values accumulator, currentValue, index, array, which are the initial input parameter, the current element, the current element's position, and the initial array respectively.

reduce(function callbackFn(accumulator, currentValue, index, array) { ... }, initialValue)

The theory is verbose, but I will take a simple example first, below is an example using reduce to calculate the sum of the numbers in the array:

const arr = [1, 2, 3, 4];
const sum = arr.reduce((accumulator, currentValue) => accumulator + currentValue, 0);
console.log(sum); // 10

0 is the initial input value corresponding to accumulator, then after each iteration, it performs the operation accumulator + currentValue, the result is assigned back to accumulator. So accumulator is the accumulated value after each iteration and it is also the final result reduce will return.

A more complex example, I don't use filter to filter out a list of users with age >= 20, instead, I will use reduce:

const usersFilter = users.reduce((accumulator, currentValue) => {
  if (currentValue.age >= 20) {
    return [...accumulator, { ...currentValue }];
  } else return accumulator;
}, []);

// or even shorter
const usersFilter = users.reduce((accumulator, currentValue) => currentValue.age >= 20 ? [...accumulator, { ...currentValue }] : accumulator, []);

With this way, I can avoid referencing as when using filter, although it is a bit verbose.

Two levels when using callback function

In the examples, I wrote the callback function directly into the functions, which makes me write code faster but in return, you will have longer code snippets that can sometimes be difficult for the reader to understand.

I call the way of writing as mentioned above as level 1. So how would you write at level 2?

That's how you write with currying, if you don't know about curry, you can refer to the article titled What is a Curry Function? A delicious "curry" dish and how to enjoy it?.

For example, I solve the initial requirement of increasing the age of each user by 1:

const incAge = item => ({...item, age: item.age + 1});
const usersIncAge = users.map(incAge);

I create a function incAge that is responsible for receiving an object, increasing the value of the age property by 1, and then returning a completely new object. Then map accepts it as a callback to process the data. Looking at line 2, you can see the code focuses on what it is doing rather than how it is doing it...

Up to now, the data editing or filtering requirements I presented have been addressed, although it takes a little more time to write code. So let's move on to a more complex requirement: I want to group users by city_code and sort the users by descending age within each group?

I won't implement the code for this requirement because it may be quite long, instead, I propose the following solution: First, get all the city_code values and filter duplicate data (unique), then iterate through each city_code to find the matching users. Finally, iterate through each group to sort the order of users in each group.

That's the solution I thought of, if you have another way, please comment for everyone to know.

So is there a shorter way to solve this problem without needing much code? The answer is yes! Let's use lodash.

Using lodash to speed up programming

For those who don't know, lodash is a very popular data processing library, with over 50k stars on GitHub. This should give you an idea of how popular this library is.

Regarding lodash, it is a collection of utility functions for data processing on arrays, objects, even data types like strings and numbers. It has a lot of functions, you can see them on the Document page.

In lodash, there is a groupBy function for grouping and a sortBy function for sorting data. Applying it to solve my requirement of grouping by city_code:

const _ = require("lodash");
const usersGroupByCity = _.groupBy(users, "city_code");

The result will look like this:

{
  HN: [
    {
      id: 1,  
      name: 'Nguyễn Văn A',  
      age: 18,  
      status: 'active',  
      city_code: 'HN'
    },  
    {
      id: 3,  
      name: 'Phạm Thị Xuân C',  
      age: 26,  
      status: 'block',  
      city_code: 'HN'
    }
  ],  
  HCM: [
    {
      id: 2,  
      name: 'Trần Thị B',  
      age: 20,  
      status: 'active',  
      city_code: 'HCM'
    }
  ]
}

Don't forget we still have one more requirement to sort the data within each group by descending age.
In lodash, there is a function called orderBy that can help me sort the data in ascending or descending order:

const _ = require("lodash");
const usersGroupByCity = _.groupBy(users, "city_code");
const orderByAge = _.mapValues(usersGroupByCity, o => {
  return _.orderBy(o, "age", "desc");
});

mapValues is a function that allows modifying the value of each attribute in the object. Here, I use mapValues to sort the data in each group.

Lodash also supports the chain function, which is a chain of successive functions. The output of this function will be the input of the next function. It can be said that it is like the compose/pipe functions that I have written about in the article Implement more efficiently with compose & pipe functions in Javascript.

const _ = require("lodash");
const orderByAgeDesc = o => _.orderBy(o, "age", "desc");

const result = _(users).groupBy("city_code").mapValues(orderByAgeDesc).value();

Both ways address the problem, however, way 1 focuses on what it is doing while way 2 focuses more on how it is doing it.

Conclusion

Data processing is an integral part of programming, it involves tasks such as adding/modifying/deleting data to serve a specific purpose.

For Array data type, Javascript has the trio map, filter, reduce as powerful assistants in processing. However, there are many other data processing utility functions that can be mentioned such as lodash. Integrating lodash will help reduce the time to rewrite code while also increasing the time to understand the code.

Premium
Hello

5 profound lessons

Every product comes with stories. The success of others is an inspiration for many to follow. 5 lessons learned have changed me forever. How about you? Click now!

Every product comes with stories. The success of others is an inspiration for many to follow. 5 lessons learned have changed me forever. How about you? Click now!

View all

Subscribe to receive new article notifications

or
* The summary newsletter is sent every 1-2 weeks, cancel anytime.

Comments (0)

Leave a comment...