JavaScript Array Operations with Examples

JavaScript Array Operations with Examples

This article illustrates the operations of JavaScript Array. These examples include built-in APIs and some custom functions, such as groupBy, union, objectToArray, etc.

Note if fruits is not initialized in examples, it is:

let fruits = ['Apple', 'Banana'];

Basic operations

Create

let fruits = ['Apple', 'Banana'];
let length = fruits.length;
// 2

let fruits1 = [];
fruits1.push('Apple'); // ['Apple']
fruits1.push('Banada');// ['Apple', 'Banana'];

let fruits2 = new Array('Apple', 'Banana');

// Creates an array of 2 empty slots, not slots with actual undefined values
let fruit3 = new Array(2); 
console.log(fruits3);
// Array [undefined, undefined]

Create an two-dimensional array

let board = [ 
  ['A0','A1','A2'],
  ['B0','B1','B2'],
  ['C0','C1','C2']];

Set values

Set values directly

fruits[1] = 'Blackberry';

If you set a value at a new indexed position which is not contained in the array. The length of the array will be updated accordingly.

let fruits = ['Apple', 'Banana'];
fruits[4] = 'Damson';
console.log(fruits); // ['Apple', 'Banana', undefined, undefined, 'Damson'];
console.log(furits.length); // 5
console.log(Object.keys(fruits));  // ['0', '1', '4']

**Fill values **

The fill() method fills (modifies) all the elements of an array from a start index (default zero) to an end
index (default array length) with a static value. It returns the modified array.

Syntax: fill(value[, start[, end]])

let array1 = [1, 2, 3, 4];

// fill with 0 from position 2 until position 4
console.log(array1.fill(0, 2, 4));
// expected output: [1, 2, 0, 0]

// fill with 5 from position 1
console.log(array1.fill(5, 1));
// expected output: [1, 5, 5, 5]

console.log(array1.fill(6));
// expected output: [6, 6, 6, 6]

Access

Access an element through index

let first = fruits[0];
// Apple

let last = fruits[fruits.length - 1];
// Banana

Note: It is possible to quote the array indexes, such as fruits['0'], although it is not necessary. The 0 in fruits[0] is coerced into a string by the JavaScript engine through an implicit toString conversion.

Get index of an element

let index = fruits.indexOf('Banana');
// 1

Add

Add to the end of an array

// push() changes the length and returns the new length
let newLength = fruits.push('Coconut'); // ["Apple", "Banana", "Coconut"]
// 3

Add to the front of an array

// unshift() changes the length and returns the new length
let newLength = fruits.unshift('Avocado');// ["Avocado", "Apple", "Banana"];
// 3

Add by setting a value at a new index position

let fruits = ['Apple', 'Banana'];
fruits[4] = 'Damson';
console.log(fruits); // ['Apple', 'Banana', undefined, undefined, 'Damson'];
console.log(furits.length); // 5
console.log(Object.keys(fruits));  // ['0', '1', '4']

When setting a property on a JavaScript array when the property is an valid array index and that index is outside the current bounds of the array, the engine will update the array’s length property accordingly.

Add a value at a specific position

The splice() method changes the contents of an array by removing or replacing existing elements and/or adding new elements in place.

let fruits = ['Apple', 'Banada', 'Coconut'];

// Remove 0 item from index of 1, and adding new item 'Blueberry'
let deletedItems = fruits.splice(1, 0, 'Blueberry'); // Add at index 
// deletedItems: []
// fruits: ['Apple', 'Blueberry', Banada', 'Coconut']

Remove

Remove from the end of an array

// pop() returns the removed element, it changes the length
// remove Banada from the end
let last = fruits.pop(); // ["Apple"];
// Banada

Remove from the front of an array

// shift() returns the removed element, it changes the length
// remove Apple from the front
let first = fruits.shift(); // ["Banana"];
// Apple

Loop

There are several ways to loop over an array. Use mapreducefilter functions first if they satisfy your needs, otherwise use below loop operations. Although these function are slower, they are easier to code、read , maintain and test. See for vs for each vs (map, reduce, filter, find) for details.

Use for loop

for (let i = 0; i < furits.length; ++i) {
    console.log(furits[i]);
}

Use forEach method

The forEach() method executes a provided callback function once for each array element.

// The parameters of index and array are optional
fruits.forEach(function(currenValue, index, array) {
  console.log(curentValue, index);
});
// "Apple" 0
// "Banada" 1

Note: It is not invoked for index properties that have been deleted or are uninitialized. For example:

const arraySparse = [1,3,,7];
let numCallbackRuns = 0;

arraySparse.forEach(function(element){
  console.log(element);
  numCallbackRuns++;
});
console.log(arraySparse); // 3

Formatted print

Use console.table

console.table displays tabular data as a table. This feature is available in Web Workers.

console.table(['Apple', 'Banada']);
console.table(board); // prints a two-demensional array

Write your own function

function printArrayElements(element, index, array) {
  console.log('a[' + index + '] = ' + element);
}

fruits.forEach(printArrayElements);
// a[0] = "Apple"
// a[1] = "Banada"

Other Operations

Copy

Copy a new array

The slice(begin, end) method returns a shallow copy of a portion of an array into a new array object (end not included) . The original array will not be modified.

let fruits = ['Apple', 'Banada'];
let shallowCopy = fruits.slice(); // this is how to make a copy
// ["Apple", "Banada"]

Syntax: slice([begin[, end]]).

Copy elements to the same array

The copyWithin(target, start, end) method shallow copies part of an array to target location in the same array and returns it without modifying its length.

Syntax: copyWithin(target[, start[, end]]).

let array1 = ['a', 'b', 'c', 'd', 'e'];

// copy to index 0 the element at index 3
console.log(array1.copyWithin(0, 3, 4));
// expected output: Array ["d", "b", "c", "d", "e"]

// copy to index 1 all elements from index 3 to the end
console.log(array1.copyWithin(1, 3));
// expected output: Array ["d", "d", "e", "d", "e"]

Filter

The filter() method creates a new array with all elements that pass the test implemented by the provided function.

let fruits = ['Apple', 'Banana', 'Blueberry'];
let bfruits = fruits.filter(val => val.startsWith('B'));
// ['Banana', 'Blueberray']

Find

Find the index

findIndex()method returns the index of the first element in the array that satisfies the provided testing function.

let furits = ['Apple', 'Banana'];

function startsWithB(element) {
  return element.startsWith('B');
}

console.log(fruits.findIndex(startsWithB));
// expected output: 1

Find the element

The find() method returns the value of the first element in the array that satisfies the provided testing function. Otherwise undefined is returned.

let array1 = [5, 12, 8, 130, 44];

let elemnent = array1.find(function(element) {
  return element > 10;
});

console.log(element);
// 12

Check whether an array includes a certain value

The includes() method determines whether an array includes a certain value among its entries, returning true or false as appropriate.

Syntax: includes(valueToFind[, fromIndex])

let fruits = ['Apple', 'Banana'];
console.log(fruits.includes('Orange'));// false
console.log(fruits.includes('App'));// false

Note: When comparing strings and characters, includes() is case-sensitive.

Note: Technically speaking, includes() uses the sameValueZero algorithm to determine whether the given element is found.

Join

The join(sperator) method creates and returns a new string by concatenating all of the elements in an array (or an array-like object), separated by commas or a specified separator string

let fruits = ['Apple', 'Banada'];

console.log(fruits.join());
// expected output: "Apple,Banada"

console.log(elements.join('-'));
// expected output: "Apple-Banada"

Merge / Concat

The concat()method is used to merge two or more arrays. This method does not change the existing arrays, but instead returns a new array.

let array1 = ['a', 'b', 'c'];
let array2 = ['d', 'e', 'f'];

console.log(array1.concat(array2));
// Array ["a", "b", "c", "d", "e", "f"]

Sort

Reverse

The reverse() method reverses an array in place. The first array element becomes the last, and the last array element becomes the first.

let array1 = ['one', 'two', 'three'];
let reversed = array1.reverse();
console.log(array1); // Array ['three', 'two', 'one']
console.log(reversed); // Array ['three', 'two', 'one']

Check/Test

Every: Test whether all elements pass provided function

The every() method tests whether all elements in the array pass the test implemented by the provided function.

function isBelowThreshold(currentValue) {
  return currentValue < 40;
}

let array1 = [1, 30, 39, 29, 10, 13];
console.log(array1.every(isBelowThreshold));
// true

Note: This method returns true for any condition put on an empty array.

Some: Test whether at least some elements pass provided function

The some() method tests whether at least one element in the array passes the test implemented by the provided function.

let even = function(element) {
  // checks whether an element is even
  return element % 2 === 0;
};

let array = [1, 2, 3, 4, 5];
console.log(array.some(even));
// true

Note: This method returns false for any condition put on an empty array.

isArray

The Array.isArray() method determines whether the passed value is an Array.

Array.isArray([1, 2, 3]);  // true
Array.isArray({foo: 123}); // false
Array.isArray('foobar');   // false
Array.isArray(undefined);  // false

isArray vs instance of

When checking for Array instance, Array.isArray is preferred over instanceof because it works through iframes.

String to Array, Map to Array, Set to Array

(Array.from('foo'));
// ['f', 'o', 'o']

The Array.from() method creates a new, shallow-copied Array instance from an array-like or iterable object.

Array.from() lets you create Arrays from:

  • array-like objects (objects with a length property and indexed elements) or
  • iterable objects (objects where you can get its elements, such as Map and Set)
const set = new Set(['foo', 'bar', 'baz', 'foo']);
Array.from(set);
// ['foo', 'bar', 'baz']

Other loops: map vs reduce

map

map() method creates a new array with the results of calling a provided function on every element in the calling array.

Syntax:

let new_array = arr.map(function callback(currentValue[, index[, array]]) {
    // Return element for new_array
}[, thisArg]
  • currentValue, the current value in the array
  • index
  • array, the array itself
  • thisArg, Value to use as this when executing callback.

Examples:

[1, 4, 9].map((val) => {
  return val * 2;
});
// It creates a new array: [2, 8, 18]

reduce

The reduce() method executes a callback on each element of the array, resulting in a single output value.

Syntax:

arr.reduce(callback(accumulator, currentValue[, index[, array]])[, initialValue])
  • accumulator

    The accumulator accumulates the callback’s return values. It is the accumulated value previously returned in the last invocation of the callback, or initialValue.

  • currentValue, the current element being processed in the array.

  • index, the index of the current element being processed in the array.

  • array, the array reduce() was called upon.

  • initialValue

    A value to use as the first argument to the first call of the callback. If no initialValue is supplied, the first element in the array will be used and skipped. Calling reduce() on an empty array without an initialValue will throw a TypeError.

Examples:

// Get sum of an array
[0, 1, 2, 3, 4].reduce(function(accumulator, currentValue, currentIndex, array) {
  return accumulator + currentValue;
});

Custom operations

Filter an Objects Array by fields

filter is an object which defines the filter condition. If an object in the array has the properties with the same values in filter, it will be filtered out.

const filterArray = (arr, filter) => {
  var data = [];
  for (let obj of arr) {
    let filtered = true;
    for (let p in filter) { // for each property in filter
      if (obj[p] != filter[p]) {
        filtered = false;
        break;
      }
    }

    if (filtered) {
      data.push(obj);
    }
  } // for

  return data;
}

Examples:

let employees = [
  {name: 'Jane', gender: 'Female'},
  {name: 'Jim', gender: 'Male' },
  {name: 'Lily', gender: 'Female'}
};
filterArray(employees, {gender: 'Female'});
// [{name: 'Jane', gender: 'Female'}, {name: 'Lily', gender: 'Female'}]

GroupBy

The following groupBy groups objects in an array by a field.

const groupBy = (arr, field) => {
  return arr.reduce(function (acc, obj) {
    let key = obj[field];
    acc[key] = acc[key] || [];
    acc[key].push(obj);
    return acc;
  }, {});
}

Examples:

let employees = [
  {name: 'Jane', gender: 'Female'},
  {name: 'Jim', gender: 'Male' },
  {name: 'Lily', gender: 'Female'}
];
groupBy(employees, 'gender');
// { 
//   'Female': [{name: 'Jane', gender: 'Female'}, {name: 'Lily', gender: 'Female'}],
//   'Male': [{name: 'Jim', gender: 'Male' }]
// }

If you want an array of grouped objects:

const groupBy = (arr, field) => {
  let groups = arr.reduce((acc, obj) => {
    let key = obj[field];
    acc[key] = acc[key] || [];
    acc[key].push(obj);
    return acc;
  }, {});

  return Object.keys(groups).map( group => {
    return groups[group];
  });
}

The result will be:

// [
//   [{name: 'Jane', gender: 'Female'}, {name: 'Lily', gender: 'Female'}],
//   [{name: 'Jim', gender: 'Male' }]
// ]

A more generic version

Below code (from javascript codesnipt) is a more generic version. It accepts an array of basic types or objects. It has 2 processes:

  1. Use map to generate a new array. Here arr is the array, and f can be a function or a field name by which it is grouped.
  2. Use reduce function to produce a object in which a field indicates a group.
const groupBy = (arr, f) =>
  arr.map(typeof f === 'function' ? f : val => val[f]).reduce((acc, val, i) => {
    acc[val] = (acc[val] || []).concat(arr[i]);
    return acc;
  }, {});

Examples:

groupBy([6.1, 4.2, 6.3], Math.floor); // {4: [4.2], 6: [6.1, 6.3]}
groupBy(['one', 'two', 'three'], 'length'); // {3: ['one', 'two'], 5: ['three']}

Of course it can be completed in one loop:

const groupBy = (arr, f) => {
  return arr.reduce((acc, val) => {
    let key = (typeof f === 'function') ? f(val) : val[f]).
    acc[key] = acc[key] || [];
    acc[key].push(val);
    return acc;
  }, {});
}

Union

Union operation returns each item in any of a and b once.

const union = (a, b) => Array.from(new Set([...a, ...b]));

Examples:

union([1, 2, 3], [4, 3, 2]); 
// [1,2,3,4]

The union above works only for primitive types. You can provides a comparison function to union other types.

const unionWith = (a, b, comp) =>
  Array.from(new Set([...a, ...b.filter(x => a.findIndex(y => comp(x, y)) === -1)]));

It filters the elements that does not appear in a from b, then makes a merge operation.

Array to Set

let set = new Set(['Apple', 'Banana', 'Apple']);
// ('Apple', 'Banana')

Unique Values in Array

const uniqueValues(arr) => [...new Set(arr)];

Object to Array

This operation convert an object to an array.

let objectToArray = obj => {
  Object.keys(obj).map(val => {
    return obj[val];
  }
}

Reference:

  • JavaScript Array. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array
  • JavaScript code snippet. https://github.com/30-seconds/30-seconds-of-code

Submodules management in Git

Git’s submodule tool allows you to create a child repository as a submodule inside a repository. This article shows you how to manipulate a submodule.

Add a submodule from a remote repository

To create a new sub repository whose content does not exist in the repository:

# Add a submodule named a-submodule from an remote repository
# This command will create a .gitmodules which contains the mapping relation of
# the submodule and its remote repository.
# It also stages the changes.
$ git submodule add https://github.com/xxx/a-submodule

# Use status to check what the above command stages
$ git status
...
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        new file:   .gitmodules
        new file:   a-submodule

# You need to commit staged changes: .gitmodules and a-submodule
$ git commit -m 'add a submodule'
[master 9d7d3b3] add: add a submodule
 2 files changed, 4 insertions(+)
 create mode 100644 .gitmodules
 create mode 160000 a-submodule

As you see, Git treats the new submodule as a file with special mode (1600000) in a commit.

For a repository with submodules inside it, it is called the main repository.

Note: In new git versions, the submodule’ s .git data is stored the main repository’s .git directory while they stay in the submodule’s directory in the old versions.

Add a submodule from an subdirectory

You can also switch a subdirectory to a submodule. To add a submodule from an subdirectory which has been tracked, you need to remove it first:

# First remove the subdirectory named csvlib from index
$ git rm --cached csvlib

Then add a subdirectory as a submodule:

# Init csvlib as a repo
$ cd csvlib
$ git init
$ git add .
$ git commit -m 'initial commit'

# Add csvlib repo as a submodule
$ git submodule add ./csvlib/
Adding existing repo at 'csvlib' to the index

# Check status
$ git status
...
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        modified:   .gitmodules
        new file:   csvlib
        deleted:    csvlib/csv.py

# Commit the changes
$ git commit -m 'switch subdirectory csvlib as a submodule'
[master 9d7d3b3] add: add submodule git-submodule
 2 files changed, 4 insertions(+)
 create mode 100644 .gitmodules
 create mode 160000 a-submodule

Note: If you switch to a branch which the subdirectory not being as a submodule,you may get an error for the subdirectory will be overwritten. At this time, an option -f is needed to checkout. Afterwards if you get back to the branch treating it as a submodule, you need to execute git checkout . in the submodule’s directory to get the content back. See more on git submodules.

Commit changes for a submodule

To commit changes in a submodule, first commit in its own repository and then commit for the main repository:

# Commit changes in the submodule directory
$ git add a.py 
$ git commit -m 'feat: add some functionly'
# Push changes to its remote repository
$ git push

# Switch to the main repository directory
$ cd ..
# Check changes from a submodule
$ git status
...
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)
  (commit or discard the untracked or modified content in submodules)

        modified:   a-submodule (modified content)

# Commit the submodule's changes to the top repository
$ git add a-submodule
$ git commit -m 'update changes of submodule a-submodule'
# Push changes if you want
$ git push

Pull updates for a submodule

To pull updates from remote for a submodule, an easy way is to run:

# Let Git to go into the submodule's directory and get updates for a-submodule
# --merge, merge local work
$ git submodule update --remote --merge a-submodule

Or you can do it manually:

# Switch to the submodule's directory
$ cd a-submodule

# Pull and merge updates form upstream branch
$ git pull
$ git merge origin/master

Manipulate multiple submodules at a time

Git has a submodule foreach command for you to manipulate multiple submodules. For example, stashing changes for all your submodules:

# Stash changes for all submodules
$ git submodule foreach 'git stash'

Remove a submodule from a repository

To remove a submodule named a-submodule from a repository:

# Frist remove the from index
$ git rm --cached a-submodule

# If you want to keep submodule's files in working tree.
# Rename it for the next command will delete it from working tree
$ mv a-submodule a-submodule-renamed

# Unregister the submodule
# This command removes it both from .git/config and working tree
$ git submodule deinit a-submodule

Adding categories navigation to WordPress header

To show categories in the header part of your website, create a new division in the header where you want them to display.

Before you start, you’d better create a child theme to make customization instead of directly modifying a theme. Take the default “twentyseventeen” theme for example, add below code to file templates-parts/header/site-branding.php of the theme:

       <div id="category-list">
            <ul>
                <li><a title="Home Page" href="index.php">home</a></li>
                <?php wp_list_categories(array('title_li' => '', 'echo' => 1)); ?>
            </ul>
        </div><!-- .category-list -->

wp_list_categories is used to display the categories list. You can use many parameters to control its result, below are some of them:

  • title_li, text for the list title <li> element

  • echo, display markup marks.

  • separator, separator between links.
  • exclude, array of strings to exclude.

Add below code in style.css to style the categories:

/* category list in header */
#category-list {
    margin: 0;
    display: inline-block;
    float: right;
    vertical-align: middle;
    font-size: 14px;
    font-style: normal;
    text-transform: capitalize;
}
#category-list li {
    display: inline; 
    list-style-type: none; 
    padding: 0px 5px;
}
#category-list a:link, #category-list a:visited {
    color: #eee;
}
#category-list a:hover {
    opacity: 0.7;
}

The display and list-style-type make the list a horizontal layout.

Here is the header with categories:

categories in header

Listing changed files in a commit

Use below command to list the changed files in a commit:

# List changed files in the specified commit
$ git show --name-only <commit>

# Examples

# List changed files in HEAD
$ git show --name-only HEAD
commit ea34837d870e48106ae9ad09f41297a64ad6a6a1 (HEAD -> master)
Author: xxx <xxx@xxx.com>
Date:   Wed Mar 13 20:27:46 2019 +0800

    feat: add localization

my-plugin.php
languages/my-plugin-zh_CN.po

If you only want the file names, use:

# List only names of changed files in HEAD
$ git diff --name-only HEAD~ HEAD
my-plugin.php
languages/my-plugin-zh_CN.po

Exporting data to csv in WordPress with PHP

In WordPress plugin development, sometimes you may need to export data to a csv file. Here we share how to implement it with PHP code.

How to export a csv file with PHP code

header() is used to send a raw HTTP header. To export a generated csv file, use below headers to tell the browser display a save dialog.

header( 'Content-Type: text/csv' ); // Supply the mime type
header( 'Content-Disposition: attachment; filename="downloaded.pdf"' ); // Supply a file name to save

Note:

There is not official RFC document for CSV files. text/csv is not a standard mime type, but it is more clear and works fine. application/octet-stream can also be used for csv files. However it is a very generous and it does not hint which application should be used to open the file.

Use two more headers to tell the content should not be cached by browser or any caches between the server and the browser.

header( "Cache-Control: no-cache, must-revalidate" );
header( "Expires: Sat, 26 Jul 1997 05:00:00 GMT" ); // Date in the past

Then echo the generated csv content directly:

echo $csv;
exit;

Note:

Any actual content must be output after header(). One common error is outputting content before header() by include or require a file. See more about header().

Below is the full example.

Example: Export data to csv in WordPress

Add an export button:

<a href="admin.php?page=export_example&export=table&noheader=1">Export</a>

Don’t forget add the argument named “noheader”. Otherwise, the csv content will directly be printed and there will be no Save dialog box popping up in the browser.

Process the export in the same file:

<?php
$table_head = array( 'column1', 'column2', 'column3' );
$table_body = array(
    array( 'a', 'b', 'c' ),
    array( 'd', 'e', 'f' )
);

// Process export
if( isset( $_GET['export'] ) ) {
    $csv = implode( $table_head, ',' );
    $csv .= "n"; // important! Make sure to use use double quotation marks.
    foreach( table_body as row ) {
        $csv .= implode( $row, ',' );
        $csv .= "n";
    }

    $filename = 'table.csv';    
    header( 'Content-Type: text/csv' ); // tells browser to download
    header( 'Content-Disposition: attachment; filename="' . $filename .'"' );
    header( 'Pragma: no-cache' ); // no cache
    header( "Expires: Sat, 26 Jul 1997 05:00:00 GMT" ); // expire date

    echo $csv;
    exit;
}
?>
<a href="admin.php?page=export_example&export=table&noheader=1">Export</a>

This example works well on Firefox, IE, Chrome.

Export data with Unicode characters to utf-8 bom csv

If your data contains Unicode characters, you may want to export a utf-8 bom csv that Excel will read properly. To achieve that, just output the BOM mark before data:

echo "xEFxBBxBF"; // UTF-8 BOM
echo $csv;
exit;