515e8846-45de-4d10-a4b5-697a74eefe0d.png

Javascript Array Methods - Demonstrated with Emojis ๐Ÿ˜œ

Intro

JavaScript arrays are versatile data structures that store multiple values in a single variable. Arrays have methods, or functions built into JavaScript that allow developers to perform various operations on arrays, such as adding, removing, or modifying elements, as well as iterating over the array. There are different types of array methods, including those that modify the original array (like push and splice) and those that return a new array or value without altering the original (like map and filter).

These methods can be incredibly useful when working with JSON arrays in Appsmith. For example, you might use filter to display only certain items from a data set, map to transform data into a more suitable format for display, or reduce to compute aggregate values. This guide will cover the basics of array methods, and how to use them in Appsmith to filter and transform data. 

Methods

Arrays can store various data types, including numbers (123), strings ("apple"), objects ({name: "John"}), boolean values (true), other arrays ([1, 2]), and even functions (function() { return 1; }). APIs and database queries in Appsmith are returned as JSON arrays, or an array of object, such as: 

[
 { "item": "Laptop", "quantity": 2, "price": 1200.00 },
 { "item": "Desk Chair", "quantity": 4, "price": 150.00 },
 { "item": "Monitor", "quantity": 3, "price": 300.00 }
]

Array methods are useful tools to transform and filter this data in Appsmith, for things like charts, drill-down tables, and moving data between datasources with different schema. When working with an array of objects like this, there are a few key array methods that are particularly useful. But first, a quick review of the basics, using an array of strings. 

Given the following array:

const fruits = ["๐ŸŽ", "๐ŸŠ", "๐ŸŒ", "๐Ÿ‰", "๐Ÿ‡"];

This data can be joined with another array using the concat() method (short for concatenate). 

const updatedFruits = fruits.concat(["๐Ÿ“", "๐Ÿ"]); 
// return value: ["๐ŸŽ", "๐ŸŠ", "๐ŸŒ", "๐Ÿ‰", "๐Ÿ‡", "๐Ÿ“", "๐Ÿ"]

This creates a new array, so the original fruits array is still just 5 items. Alternatively, you could use the push() method to add the new values:

fruits.push(["๐Ÿ“", "๐Ÿ"]); 
// return value: 7 (new arrray length)
console.log(fruits);
// return value: ["๐ŸŽ", "๐ŸŠ", "๐ŸŒ", "๐Ÿ‰", "๐Ÿ‡", "๐Ÿ“", "๐Ÿ"]

However, there are a few differences:

  • push modifies the original array, so there's no need to declare it as a new variable updatedFruits. You can push values to the fruits array directly. 
  • push returns the new array length, instead of the new array. 

In general, array methods either return a new array, modify the original array, or return some other value related to the original array, like the value at a certain index or that matches a certain condition. 

Here's a quick review of some more common array methods using our fruits array, and the expected return values. 

const fruits = ["๐ŸŽ", "๐ŸŠ", "๐ŸŒ", "๐Ÿ‰", "๐Ÿ‡"];

fruits.at(1); // ๐ŸŠ
fruits.concat(["๐Ÿ“", "๐Ÿ"]); // ["๐ŸŽ", "๐ŸŠ", "๐ŸŒ", "๐Ÿ‰", "๐Ÿ‡", "๐Ÿ“", "๐Ÿ"]
fruits.every(fruit => fruit === "๐ŸŽ"); // false
fruits.fill("๐Ÿ’"); // ["๐Ÿ’", "๐Ÿ’", "๐Ÿ’", "๐Ÿ’", "๐Ÿ’"]
fruits.filter(fruit => fruit === "๐ŸŒ"); // ["๐ŸŒ"]
fruits.find(fruit => fruit === "๐ŸŒ"); // ๐ŸŒ
fruits.findIndex(fruit => fruit === "๐ŸŒ"); // 2
fruits.forEach(fruit => console.log(fruit)); // Logs each fruit
fruits.includes("๐ŸŒ"); // true
fruits.indexOf("๐ŸŒ"); // 2
fruits.join("-"); // "๐ŸŽ-๐ŸŠ-๐ŸŒ-๐Ÿ‰-๐Ÿ‡"
fruits.lastIndexOf("๐ŸŒ"); // 2
fruits.map(fruit => fruit + "๐Ÿ’"); // ["๐ŸŽ๐Ÿ’", "๐ŸŠ๐Ÿ’", "๐ŸŒ๐Ÿ’", "๐Ÿ‰๐Ÿ’", "๐Ÿ‡๐Ÿ’"]
fruits.pop(); // "๐Ÿ‡"
fruits.push("๐Ÿ“"); // ["๐ŸŽ", "๐ŸŠ", "๐ŸŒ", "๐Ÿ‰", "๐Ÿ‡", "๐Ÿ“"]
fruits.reverse(); // ["๐Ÿ‡", "๐Ÿ‰", "๐ŸŒ", "๐ŸŠ", "๐ŸŽ"]
fruits.shift(); // ["๐ŸŠ", "๐ŸŒ", "๐Ÿ‰", "๐Ÿ‡"]
fruits.slice(1, 3); // ["๐ŸŠ", "๐ŸŒ"]
fruits.some(fruit => fruit === "๐ŸŒ"); // true
fruits.splice(1, 1, "๐Ÿ’"); // ["๐ŸŽ", "๐Ÿ’", "๐ŸŒ", "๐Ÿ‰", "๐Ÿ‡"]
fruits.toString(); // "๐ŸŽ,๐ŸŠ,๐ŸŒ,๐Ÿ‰,๐Ÿ‡"
fruits.unshift("๐Ÿ"); // ["๐Ÿ", "๐ŸŽ", "๐ŸŠ", "๐ŸŒ", "๐Ÿ‰", "๐Ÿ‡"]

This format makes it easy to see at a glance what all the various methods return. 

Array of Objects

Now let's apply this to an array of objects, like an Appsmith API or database query would return. 

const "fruits" = [
    {"name": "Apple", "icon": "๐ŸŽ"},
    {"name": "Orange", "icon": "๐ŸŠ"},
    {"name": "Banana", "icon": "๐ŸŒ"},
    {"name": "Watermelon", "icon": "๐Ÿ‰"},
    {"name": "Grapes", "icon": "๐Ÿ‡"}
  ]
  
  // Example using filter
// Find all fruits with names that contain more than 5 characters
const longNamedFruits = fruits.filter(fruit => fruit.name.length > 5);
console.log(longNamedFruits);
// Output: [{"name": "Orange", "icon": "๐ŸŠ"}, {"name": "Banana", "icon": "๐ŸŒ"}, {"name": "Watermelon", "icon": "๐Ÿ‰"}]

// Example using find
// Find the first fruit with the name "Banana"
const banana = fruits.find(fruit => fruit.name === "Banana");
console.log(banana);
// Output: {"name": "Banana", "icon": "๐ŸŒ"}

// Example using map
// Create an array of fruit names
const fruitNames = fruits.map(fruit => fruit.name);
console.log(fruitNames);
// Output: ["Apple", "Orange", "Banana", "Watermelon", "Grapes"]

// Example using reduce
// Create a string of all fruit icons
const fruitIcons = fruits.reduce((icons, fruit) => icons + fruit.icon, "");
console.log(fruitIcons);
// Output: "๐ŸŽ๐ŸŠ๐ŸŒ๐Ÿ‰๐Ÿ‡"

Practical Examples in Appsmith

Now that we have the basics out of the way, let's look at a few practical examples that cover common use cases in Appsmith. 

Select Widgets

The select widget uses an array of objects to provide the list of options. Each object should have a property to use for the label and the value. 

 

This allows you to show one value for the user to select from the dropdown (like a name or email), and another value that gets saved under the hood (like a user ID). 

However, your data may not be in the same format. Here's how you can map the data to the correct format for the select widget options:

const fruits = [
  {"name": "Apple", "icon": "๐ŸŽ"},
  {"name": "Orange", "icon": "๐ŸŠ"},
  {"name": "Banana", "icon": "๐ŸŒ"},
  {"name": "Watermelon", "icon": "๐Ÿ‰"},
  {"name": "Grapes", "icon": "๐Ÿ‡"}
];

// Map fruits to the desired format directly
const mappedFruits = fruits.map(fruit => ({
  label: fruit.icon,
  value: fruit.name
}));

console.log(mappedFruits);
// Output: [
//   { label: "๐ŸŽ", value: "Apple" },
//   { label: "๐ŸŠ", value: "Orange" },
//   { label: "๐ŸŒ", value: "Banana" },
//   { label: "๐Ÿ‰", value: "Watermelon" },
//   { label: "๐Ÿ‡", value: "Grapes" }
// ]

NOTE: The select widget was recently updated to allow other property names besides label & value, however, select fields in a table or JSONForm are a little more specific on the data structure. As a best practice, you should always map your data to this {label, value} format so the same function can be used to supply all select widget, whether stand-alone, or in a table or JSON form. 

Calculating Totals Using a For Loop, and Reduce

Now let's return to the first example data, which is a bit more like what you would get from an API or database. Here we have a set of line items from an order, and we need to calculate the total. This can be done with any looping method like a for loop, or by using reduce

const orderItems = [
  { "item": "Laptop", "quantity": 2, "price": 1200.00 },
  { "item": "Desk Chair", "quantity": 4, "price": 150.00 },
  { "item": "Monitor", "quantity": 3, "price": 300.00 }
];

// Calculate order total using a loop
let totalWithLoop = 0;
for (let i = 0; i < orderItems.length; i++) {
  totalWithLoop += orderItems[i].quantity * orderItems[i].price;
}
console.log("Total with loop:", totalWithLoop);
// Output: Total with loop: 3900

// Calculate order total using reduce
const totalWithReduce = orderItems.reduce((total, item) => {
  return total + (item.quantity * item.price);
}, 0);
console.log("Total with reduce:", totalWithReduce);
// Output: Total with reduce: 3900

NOTE: If your datasource is an SQL or NoSQL datasource that supports aggregate queries like SUM(), it's more performant to let the server calculate and return the total instead of using JavaScript on the client side. Check out this video on SQL best practices for more info. 

Using the Lodash Library Methods

Lodash is a JavaScript library included with Appsmith that provides a wide range of methods for working with arrays and objects. Many tasks like filtering and transforming data multiple lines of JavaScript but can be done in a single line with Lodash. It's best to familiarize yourself with the native JavaScript methods first, then explore how Lodash can help simplify your code. Here's the same total calculated with Lodash. 

const orderItems = [
  { "item": "Laptop", "quantity": 2, "price": 1200.00 },
  { "item": "Desk Chair", "quantity": 4, "price": 150.00 },
  { "item": "Monitor", "quantity": 3, "price": 300.00 }
];

// Calculate order total using lodash
const totalWithLodash = _.sumBy(orderItems, item => item.quantity * item.price);
console.log(totalWithLodash);
// Output: 3900

Conclusion

Array methods offer a wide range of tools for filtering and transforming data. These methods are often helpful in processing data for display in Appsmith, and moving data between sources with different schema. By familiarizing yourself with the available methods, you will be able to write more efficient and performant code. For even more concise and readable code, consider using Lodash