Categories
Quasar

Developing Vue Apps with the Quasar Library — Table No Data Display and Custom Sorting

Quasar is a popular Vue UI library for developing good looking Vue apps.

In this article, we’ll take a look at how to create Vue apps with the Quasar UI library.

No Data Display

We can display something when no data is displayed in our table.

For instance, we can write:

<!DOCTYPE html>
<html>
  <head>
    <link
      href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900|Material+Icons"
      rel="stylesheet"
      type="text/css"
    />
    <link
      href="https://cdn.jsdelivr.net/npm/quasar@1.12.13/dist/quasar.min.css"
      rel="stylesheet"
      type="text/css"
    />
  </head>
  <body class="body--dark">
    <script src="https://cdn.jsdelivr.net/npm/vue@^2.0.0/dist/vue.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/quasar@1.12.13/dist/quasar.umd.min.js"></script>
    <div id="q-app">
      <q-layout
        view="lHh Lpr lFf"
        container
        style="height: 100vh;"
        class="shadow-2 rounded-borders"
      >
        <div class="q-pa-md">
          <q-table
            title="Treats"
            :data="data"
            :columns="columns"
            :filter="filter"
            no-data-label="I didn't find anything for you"
            no-results-label="The filter didn't uncover any results"
            row-key="name"
          >
            <template v-slot:top-right>
              <q-input
                borderless
                dense
                debounce="300"
                v-model="filter"
                placeholder="Search"
              >
                <q-icon slot="append" name="search"></q-icon>
              </q-input>
            </template>

            <template v-slot:no-data="{ icon, message, filter }">
              <div>No data</div>
            </template>
          </q-table>
        </div>
      </q-layout>
    </div>
    <script>
      const columns = [
        {
          name: "name",
          required: true,
          label: "Dessert",
          align: "left",
          field: (row) => row.name,
          format: (val) => `${val}`,
          sortable: true
        },
        {
          name: "calories",
          align: "center",
          label: "Calories",
          field: "calories",
          sortable: true
        },
        { name: "fat", label: "Fat (g)", field: "fat", sortable: true },
        {
          name: "calcium",
          label: "Calcium (%)",
          field: "calcium",
          sortable: true,
          sort: (a, b) => parseInt(a, 10) - parseInt(b, 10)
        }
      ];
      const data = [
        {
          name: "Frozen Yogurt",
          calories: 159,
          fat: 6.0,
          calcium: "14%"
        },
        {
          name: "Ice cream sandwich",
          calories: 237,
          fat: 9.0,
          calcium: "8%"
        },
        {
          name: "Eclair",
          calories: 262,
          fat: 16.0,
          calcium: "6%"
        },
        {
          name: "Honeycomb",
          calories: 408,
          fat: 3.2,
          calcium: "0%"
        },
        {
          name: "Donut",
          calories: 452,
          fat: 25.0,
          calcium: "2%"
        },
        {
          name: "KitKat",
          calories: 518,
          fat: 26.0,
          calcium: "12%"
        }
      ];
      new Vue({
        el: "#q-app",
        data: {
          columns,
          data,
          filter: ""
        }
      });
    </script>
  </body>
</html>

to add that.

We populate the no-data slot with the content we want to show when there’s no data displayed in the table.

Hide Bottom Bar

We can hide the bottom bar by adding the hide-bottom prop:

<!DOCTYPE html>
<html>
  <head>
    <link
      href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900|Material+Icons"
      rel="stylesheet"
      type="text/css"
    />
    <link
      href="https://cdn.jsdelivr.net/npm/quasar@1.12.13/dist/quasar.min.css"
      rel="stylesheet"
      type="text/css"
    />
  </head>
  <body class="body--dark">
    <script src="https://cdn.jsdelivr.net/npm/vue@^2.0.0/dist/vue.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/quasar@1.12.13/dist/quasar.umd.min.js"></script>
    <div id="q-app">
      <q-layout
        view="lHh Lpr lFf"
        container
        style="height: 100vh;"
        class="shadow-2 rounded-borders"
      >
        <div class="q-pa-md">
          <q-table
            title="Treats"
            :data="data"
            :columns="columns"
            row-key="name"
            hide-bottom
          >
          </q-table>
        </div>
      </q-layout>
    </div>
    <script>
      const columns = [
        {
          name: "name",
          required: true,
          label: "Dessert",
          align: "left",
          field: (row) => row.name,
          format: (val) => `${val}`,
          sortable: true
        },
        {
          name: "calories",
          align: "center",
          label: "Calories",
          field: "calories",
          sortable: true
        },
        { name: "fat", label: "Fat (g)", field: "fat", sortable: true },
        {
          name: "calcium",
          label: "Calcium (%)",
          field: "calcium",
          sortable: true,
          sort: (a, b) => parseInt(a, 10) - parseInt(b, 10)
        }
      ];
      const data = [
        {
          name: "Frozen Yogurt",
          calories: 159,
          fat: 6.0,
          calcium: "14%"
        },
        {
          name: "Ice cream sandwich",
          calories: 237,
          fat: 9.0,
          calcium: "8%"
        },
        {
          name: "Eclair",
          calories: 262,
          fat: 16.0,
          calcium: "6%"
        },
        {
          name: "Honeycomb",
          calories: 408,
          fat: 3.2,
          calcium: "0%"
        },
        {
          name: "Donut",
          calories: 452,
          fat: 25.0,
          calcium: "2%"
        },
        {
          name: "KitKat",
          calories: 518,
          fat: 26.0,
          calcium: "12%"
        }
      ];
      new Vue({
        el: "#q-app",
        data: {
          columns,
          data,
          filter: ""
        }
      });
    </script>
  </body>
</html>

Custom Sorting

We can add custom sorting by setting the sort-method prop to our own sorting method:

<!DOCTYPE html>
<html>
  <head>
    <link
      href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900|Material+Icons"
      rel="stylesheet"
      type="text/css"
    />
    <link
      href="https://cdn.jsdelivr.net/npm/quasar@1.12.13/dist/quasar.min.css"
      rel="stylesheet"
      type="text/css"
    />
  </head>
  <body class="body--dark">
    <script src="https://cdn.jsdelivr.net/npm/vue@^2.0.0/dist/vue.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/quasar@1.12.13/dist/quasar.umd.min.js"></script>
    <div id="q-app">
      <q-layout
        view="lHh Lpr lFf"
        container
        style="height: 100vh;"
        class="shadow-2 rounded-borders"
      >
        <div class="q-pa-md">
          <q-table
            title="Treats"
            :data="data"
            :columns="columns"
            row-key="name"
            :sort-method="customSort"
            binary-state-sort
          >
          </q-table>
        </div>
      </q-layout>
    </div>
    <script>
      const columns = [
        {
          name: "name",
          required: true,
          label: "Dessert",
          align: "left",
          field: (row) => row.name,
          format: (val) => `${val}`,
          sortable: true
        },
        {
          name: "calories",
          align: "center",
          label: "Calories",
          field: "calories",
          sortable: true
        },
        { name: "fat", label: "Fat (g)", field: "fat", sortable: true },
        {
          name: "calcium",
          label: "Calcium (%)",
          field: "calcium",
          sortable: true,
          sort: (a, b) => parseInt(a, 10) - parseInt(b, 10)
        }
      ];
      const data = [
        {
          name: "Frozen Yogurt",
          calories: 159,
          fat: 6.0,
          calcium: "14%"
        },
        {
          name: "Ice cream sandwich",
          calories: 237,
          fat: 9.0,
          calcium: "8%"
        },
        {
          name: "Eclair",
          calories: 262,
          fat: 16.0,
          calcium: "6%"
        },
        {
          name: "Honeycomb",
          calories: 408,
          fat: 3.2,
          calcium: "0%"
        },
        {
          name: "Donut",
          calories: 452,
          fat: 25.0,
          calcium: "2%"
        },
        {
          name: "KitKat",
          calories: 518,
          fat: 26.0,
          calcium: "12%"
        }
      ];
      new Vue({
        el: "#q-app",
        data: {
          columns,
          data
        },
        methods: {
          customSort(rows, sortBy, descending) {
            const data = [...rows];

            if (sortBy) {
              data.sort((a, b) => {
                const x = descending ? b : a;
                const y = descending ? a : b;

                if (sortBy === "name") {
                  return x[sortBy] > y[sortBy]
                    ? 1
                    : x[sortBy] < y[sortBy]
                    ? -1
                    : 0;
                } else {
                  return +x[sortBy] - +y[sortBy];
                }
              });
            }

            return data;
          }
        }
      });
    </script>
  </body>
</html>

The customSort method takes the rows parameter which has all the table data.

sortBy has the field that we sort by.

And descending is a boolean that indicates whether we sort in descending order or not.

Conclusion

We can add custom sorting and no data display into our Quasar table.

Categories
JavaScript Basics

Async Iteration with JavaScript

Asynchronous code is a very important part of JavaScript. We need to write lots of asynchronous code since JavaScript is a single-threaded language. If we run everything line by line, then code that takes longer to run will hold up the program, causing it to freeze.

To write asynchronous code easily, JavaScript has promises which are chainable and can be run in sequence without nesting.

ES2017 introduced the async and await syntax for chaining promises, which makes everything easier. However, there was still no way to run promises sequentially by iterating them in sequence. This means that running lots of promises in a sequence is still a problem that has no solution yet.

Fortunately, in ES2018, we finally have the for-await-of loop that we can use with async functions that were introduced in ES2017. It works with any iterable objects like the synchronous for...of loop. This means that we can iterate through objects like Maps, Sets, NodeLists, and the arguments object with it.

Also, it can iterate through any object with the Symbol.iterator method which returns a generator to let us do the iteration.

for-await-of Loop

We can use the for-await-of loop as follows:

let promises = [];
for (let i = 1; i <= 10; i++) {
  promises.push(new Promise((resolve) => {
    setTimeout(() => resolve(i), 1000);
  }));
}

(async () => {
  for await (let p of promises) {
    const val = await p;
    console.log(val);
  }
})();

In the example above, we added 10 promises by defining them in the for loop and then pushing them to the promises array. Then we run the promises one at a time by defining an async function and then use the for-await-of loop to loop through each promise and run them one at a time.

The await in the for-await-of loop will get us the resolved value from the promise and the console.log will log the resolved value.

Like any other async functions, it can only return promises.

The syntax of the for-await-of loop is the same as any other for...of loop. The let p is the variable declaration and promises is the promises array that we’re iterating through.

The code above works like Promise.all , the code is run in parallel, so we see 1 to 10 after 1 second.

Async Iterables

Alternatively, we can define our own asynchronous iterable object if we define an object with the Symbol.asyncIterator method. It’s a special method that returns a generator lets us iterate through it with the for-await-of loop.

For example, we can write:

let asyncIterable = {
  [Symbol.asyncIterator]() {
    return {
      i: 1,
      next() {
        if (this.i <= 10) {
          return new Promise((resolve) => {
            setTimeout(() => resolve({
              value: this.i++,
              done: false
            }), 1000);
          });
        }

        return Promise.resolve({
          done: true
        });
      }
    };
  }
};

(async () => {
  for await (let p of asyncIterable) {
    const val = await p;
    console.log(val);
  }
})();

The code above will return a generator which returns a new promise as the for-await-of loop runs.

In the Symbol.asyncIterator method, we have a next function, which is required if we want to make an asynchronous iterable object. Also, we have the i property to keep track of what we iterated through. ‘

In the promise that we return, we have the setTimeout with a callback function that resolves to an object with the value we want to resolve, and the property done . The done property is false if the iterator should keep on returning new values for the for-await-of loop and true otherwise.

Async Generator

We can make the code shorter with the yield keyword to make a generator function to return a generator. Generators are another kind of iterable object which can be used with the for-await-of loop.

For example, we can write:

async function* asyncGenerator() {
  for (let i = 1; i <= 10; i++) {
    yield i;
  }
}

(async () => {
  for await (let p of asyncGenerator()) {
    const val = await p;
    console.log(val);
  }
})();

The keyword function* denotes that our function only returns a generator. async means that the generator runs asynchronously. Inside the asyncGenerator function, we have the yield keyword. The yield keyword gets us the next item from the generator as we loop through the generator with the for-await-of loop.

The yield keyword only works with top-level code. It doesn’t work inside callback functions.

If we run the code above, we should get 1 to 10 outputted.

If we write something like:

async function* asyncGenerator() {
  for (let i = 1; i <= 10; i++) {
    setTimeout(() => {
      yield i;
    }, 1000);
  }
}

We’ll get a SyntaxError .

An object can be made iterable with if we define a Symbol.asyncIterator method which is set to a generator function.

For example, we can write:

(async () => {
  const iterableObj = {
    [Symbol.asyncIterator]: async function*() {
      for (let i = 1; i <= 10; i++) {
        yield i;
      }
    }
  }

  for await (let val of iterableObj) {
    console.log(val);
  }
})();

Then we get 1 to 10 logged in the console.log output.

It works like the asyncIterable object we defined above, except that the code is much shorter. Also, we can’t use the yield keyword on callbacks so we’ve to write it like what we have above in that case.

Finally, we can write the following to loop through promises directly:

(async () => {

  for await (let i of Array.from({
    length: 10
  }, ((v, i) => i))) {
    const val = await new Promise((resolve) => {
      setTimeout(() => resolve(i), 1000)
    })
    console.log(val);
  }
})();

The await keyword actually waits for each promise to resolve until running the next one, so we get each number showing up after 1 second when we run the code.

Asynchronous iteration solves the promise of running many promises sequentially. This feature is available in ES2018 or later with the introduction of the for-await-of loop and asynchronous iterators and generators. With them, we solved the issue of iterating through asynchronous code.

Categories
JavaScript

Avoiding Shared Mutable State in JavaScript by Deep Copying Data

In JavaScript, like any other program languages, stores things in variables, which can be changed on the fly. This may be a problem because we may change things accidentally that is shared. Having lots of code share the same mutable state is hard to trace. It makes debugging and read the code hard.

For example, if we change the same array in different function as follows:

let arr = [];
const foo = () => {
  arr = [1, 2, 3];
}

const bar = () => {
  arr = [4, 5, 6];
}

Then the value of arr changes depending on whether foo or bar is called last. If foo is called then arr is [1, 2, 3] . On the other hand, if bar is called then arr is [4, 5, 6] .

This is a problem because as the code gets more complex, then the more function calls there are. If lots of functions are doing things like this, then tracing the value is hard and debugging is confusing.

Also, it’s hard to read how the logic flows as functions are called with these side effects.

In this article, we look at how to do a deep copy of data to prevent mutation of shared state in a program by making deep copies of data. Also, we look at ways to prevent the mutation of data exposed from class methods.

Deep Copy

Nested Spreading

We can use the spread operator in each level of an object to do deep copying manually.

For example, given that we have the following object:

const obj = {
  foo: {
    bar: 1,
    baz: 2
  },
  a: 3
}

We can make a deep copy of it as follows:

const objCopy = {
  foo: {
    ...obj.foo
  },
  a: obj.a
};

As we can see, this is going to be a problem when we have more levels and properties. However, we do get a deep copy of the original object.

Deep Copy Via JSON.stringify and JSON.parse

We can call JSON.stringify to return a string of an object and then call JSON.parse on the stringify to return it to the original form.

This works for all properties and values that are supported by JSON, which means that entities like Symbols and functions are excluded.

For example, we can write:

const obj = {
  foo: {
    bar: 1,
    baz: 2
  },
  a: 3
}

const objCopy = JSON.parse(JSON.stringify(obj));

objCopy will be a deep copy of obj . This is because a string if immutable and JSON.parse returns a new parsed copy of the stringified object.

Copying an Instance of a Class

We can copy an instance of a class easier than with objects. We can write a clone method that returns the instance of the object to do this.

For example, we can write the following code to make one class that inherits from another class:

class Person {
  constructor(name) {
    this.name = name;
  }
}

class Employee extends Person {
  constructor(name, employeeCode) {
    super(name);
    this.employeeCode = employeeCode;
  }

  clone() {
    return new Employee(this.name, this.employeeCode);
  }
}

Then we call clone to create a new duplicate object:

const employee = new Employee('Joe', 1);
const employeeClone = employee.clone();

console.log(employee.__proto__);
console.log(employeeClone.__proto__);

In the clone method of the Employee class, we return a new instance of an Employee .

Then in the first 2 console.log outputs, we see that both employee and employeeClone have the same prototype.

Why Does Copying Help Prevent Mutating Shared State?

Copying prevents the mutation of a shared state because we copied the shared data before we attempt to change it. This is handy because it stops us from accidentally making changes to shared data.

If we don’t make changes to shared data, then tracing it is easy. Then we won’t have to worry about accidentally having different parts of our code mutating the same state.

Copying Exposed Internal Class Data Before Making Changes

Before making changes to internal class data, we should make a copy of it and then return it in a method. This prevents us from changing the class data directly.

For example, if we have the following class:

class NumArray {
  constructor() {
    this._arr = [];
  }

  add(num) {
    this._arr.push(num);
  }

  getParts() {
    return this._arr;
  }

  toString() {
    return this._arr.join('');
  }
}

and we call the methods in various ways by writing the following:

const numArr = new NumArray();
numArr.add(1);
numArr.add(2);
console.log(numArr.toString());
numArr.getParts().length = 0;
console.log(numArr.toString());

We can see that the first console.log has the value '1,2' , and the second one logs an empty string.

This is because we changed the length property of the array exposed by the getParts methods. We set it to 0, so the array is emptied.

To prevent this, we can copy the array before returning it in getParts . We write the following instead:

class NumArray {
  constructor() {
    this._arr = [];
  }

  add(num) {
    this._arr.push(num);
  }

  getParts() {
    return [...this._arr];
  }

  toString() {
    return this._arr.join(',');
  }
}

Then when we make the same calls to the NumArray class’ methods as follows:

const numArr = new NumArray();
numArr.add(1);
numArr.add(2);
console.log(numArr.toString());
numArr.getParts().length = 0;
console.log(numArr.toString());

We get '1,2' in both console.log outputs.

We can do a deep copy of data in various ways. First we can manually copy data by repeatedly using the spread operator in every level.

Also, we can use JSON.stringify and JSON.parse to copy data that can be included in JSON.

For class instances, we can make a method that returns instances of classes by return a new instance of the data with the same data. This preserves the data and the inheritance structure.

Finally, we can prevent data exposed in class methods from being modified by making a copy of it before return it in the method.

Categories
JavaScript

Avoiding Shared Mutable State in JavaScript by Shallow Copying Data

In JavaScript, like any other program languages, stores things in variables, which can be changed on the fly. This may be a problem because we may change things accidentally that is shared. Having lots of code share the same mutable state is hard to trace. It makes debugging and read the code hard.

For example, if we change the same array in different function as follows:

let arr = [];
const foo = () => {
  arr = [1, 2, 3];
}

const bar = () => {
  arr = [4, 5, 6];
}

Then the value of arr changes depending on whether foo or bar is called last. If foo is called then arr is [1, 2, 3] . On the other hand, if bar is called then arr is [4, 5, 6] .

This is a problem because as the code gets more complex, then the more function calls there are. If lots of functions are doing things like this, then tracing the value is hard and debugging is confusing.

Also, it’s hard to read how the logic flows as functions are called with these side effects.

There’re a few ways to avoid this situation. There’s the const keyword to prevent reassignment. Also, we can copy objects to prevent the original from being modified.

Using Const for Constants

If we want to share constants between different parts of our code, then we can use the const keyword to declare constants. This prevents them from being modified.

For example, if we write:

const arr = [];
const foo = () => {
  arr = [1, 2, 3];
}

const bar = () => {
  arr = [4, 5, 6];
}

Then whenever foo or bar are called, we’ll get an error.

Shallow Copying Data

We can also prevent the original piece of data from being changed while we manipulate data in our functions by making a copy of the original.

There’re 2 ways to copy data. One is to do a shallow copy, where we copy the top-level entries of an object or array.

If we have nested arrays or objects, then we have to do a deep copy, where we copy all the levels of an object or array.

In this article, we’ll look at how to shallow copy data.

To make a shallow copy, we can use the spread operator. It works for both objects an arrays.

For example, we can write the following for objects:

let obj = {
  a: 1,
  b: 2,
  c: 3
};
let objCopy= {
  ...obj
};

And we can write the following for arrays:

let arr = [1, 2, 3];
let arrCopy = [...arr];

There’re a few limitations with using the spread operator. First, the prototype isn’t copied, so if we have things that inherit from some prototype like we have in the following code:

let obj = Object.create({
  foo: 1
})
obj.a = 1;
obj.a = 2;

let objCopy = {
  ...obj
};

console.log(obj.__proto__);
console.log(objCopy.__proto__);

We see that the first console.log output is completely different from the second. obj ‘s prorotype is {foo: 1} which we explicitly set. However, objCopy ‘s prototype is Object.prototype , which is completely different.

Special objects like regular expressions also special internal slots that aren’t copied.

Also, we can see from the log output above that inherited values aren’t copied with the spread operator.

In addition, only enumerable properties are copied. This means for instance, if we copy an array to an object with the spread operator as follows:

let arr = [1, 2];
let obj = {
  ...arr
};

console.log(arr);
console.log(obj);

We missing the length property from the second console.log ‘s output.

Finally, getters, setters and property descriptors also aren’t copied over. For example, if we write:

let obj = Object.create({
  foo: 1
})
obj.a = 1;
Object.defineProperty(obj, 'b', {
  value: 2,
  writable: false,
  enumerable: true
})

console.log(Object.getOwnPropertyDescriptors(obj))
console.log(Object.getOwnPropertyDescriptors(objCopy))

Then we that the property descriptors of obj and objCopy are different.

We can get around some of these issues. We can copy the prototype of the original object into the new object as follows:

let obj = Object.create({
  foo: 1
})
obj.a = 1;
obj.a = 2;
let objCopy = {
  __proto__: Object.getPrototypeOf(obj),
  ...obj
};
console.log(obj.__proto__);
console.log(objCopy.__proto__);

Then we get that both obj and objCopy have the same prototype in the console.log statements above.

We can copy over the value along with other property descriptors by writing the following code:

let obj = Object.create({
  foo: 1
})
obj.a = 1;
Object.defineProperty(obj, 'b', {
  value: 2,
  writable: false,
  enumerable: true
})

let objCopy = Object.defineProperties({}, Object.getOwnPropertyDescriptors(obj))

console.log(Object.getOwnPropertyDescriptors(obj))
console.log(Object.getOwnPropertyDescriptors(objCopy))

The property descriptor object includes the value of a property, so we can define all the properties with the defineProperties method and pass in the property descriptors with the getOwnPropertyDescriptors called with obj passed in to get obj ‘s property descriptors.

We should see that property b has writable set to false in the property descriptor of both obj and objCopy .

Preventing shared mutable state is a problem in JavaScript. We want to avoid this to prevent mutating shared data, which makes tracing code and debugging tough.

Copying objects in JavaScript precisely is tricky. The spread operator doesn’t do a thorough copy of an object. The property descriptors, getters and settings, and prototype aren’t copied over. Most of these issues can be solved by copying them over manually as we did with the object’s prototype and property descriptors.

For constants, we use const to prevent accidental reassignment.

Categories
JavaScript

More JavaScript Array Tips

Replacing and mapping entries

JavaScript, like any other programming language, has many handy tricks that let us write our programs more easily. In this article, we will look at how to do different things that involve arrays, like replacing specific values from an array and mapping array entries from one value to another.

Replacing Specific Value From an Array

There are a few ways to replace specific values from an array. We can use the indexOf method to get the first occurrence of an array and then use the index to assign a new value to the entry in that array index. For example, we can use the indexOf method like the following code:

const arr = ['apple', 'orange', 'grape', 'banana'];
const index = arr.indexOf('orange');
arr[index] = 'chicken';
console.log(arr);

The indexOf is called on arr and takes in any object that we want to get the first index of in the array. It works best with primitive values since it doesn’t do deep checks for objects, so it only works by checking the references for objects. In the third line, we reassigned the value of the index that is assigned by the indexOf method, which should be 1. Then, we assigned it the new value 'chicken'. Then, we should get the following output from the console.log statement on the last line:

["apple", "chicken", "grape", "banana"]

Note that we can use const to declare arr since we aren’t assigning any new value to any property of arr so it will work without errors.

We can also use the splice method to replace one or more values of an array. This method lets us removing, replace an existing element or adding new elements in place. The argument of the splice method is the starting index start of which to start changing the array. It can be positive or negative. If it’s negative, then it’ll start changing the array from the end and move towards the start of the array. The end index is -1, the second is -2 and so on. The second argument of the splice method is the deleteCount, which is an optional argument that lets us specify how many items to delete starting from the start parameter in the first element. Subsequent arguments are the items that we want to insert into an array. This can go on for as long as we want. Only the first argument is required.

We can use the splice method to first remove the entry that we want to replace by getting the index of the item that we want to replace, and then we can use the splice method to insert a new entry in its place like in the following code:

const arr = ['apple', 'orange', 'grape', 'banana'];
const index = arr.indexOf('orange');
arr.splice(index, 1);
arr.splice(index, 0, 'chicken');
console.log(arr);

If we run the code above, we should get the same output as we did before:

["apple", "chicken", "grape", "banana"]

The first two lines are the same as the first example. Then, we called the splice method the first time to remove the original entry in the index. The first argument is the index of the array we want to remove, and the second specifies that we only remove one entry, which is the entry specified by the index. Then we call splice again to insert the new entry in its place. We pass in index again in the second splice call since we want to insert the new element in the same place as the original. The second argument is zero since we don’t want to remove any entry. Then, we pass in 'chicken' in the third argument so that we get 'chicken' in the same position that 'orange' was in.

Map Array Entries From One Value to Another

If we want to map each entry of an array to a new value, we can do it in a few ways. We can either use the map method or the Array.from method do to this. The map method is an array instance method that takes a callback function that has up to three parameters. The first parameter is the value of the array that’s being processed by the map method. This is a required parameter. The second parameter is an optional parameter, which is the index of the array entry that’s being processed in the array. The third argument is the array of which the map method is being called on. The callback returns the value that we want the new value to have.

For example, if we want to get a field of each array entry into a new array, we can write the following code:

const arr = [{
    food: 'apple',
    color: 'red'
  },
  {
    food: 'orange',
    color: 'orange'
  },
  {
    food: 'grape',
    color: 'purple'
  },
  {
    food: 'banana',
    color: 'yellow'
  }
];
const foodColors = arr.map(({
  color
}) => color);
console.log(foodColors);

In the code above, we used the map method to get the value of the color field and put it in a new array. In the map method, we passed in a callback function with the first parameter, with the objects in the arr array destructured into color variable, and the color variable is retrieved within the parameter then we returned it. This is will get us the value of the color field of each entry into the new foodColors array.

Alternatively, we can use the Array.from method to do the same thing. The Array.from method creates a new shallow copied array instance from an array-like or other iterable objects. The first argument that it accepts is an array or other array-like or iterable objects like NodeList, arguments , strings, TypedArrays like Uinit8Array, Map, other Sets, and any other object that have a Symbol.iterator method. The second argument is an optional callback argument function we can use to map each entry from one value to another. The callback function takes two parameters, which is the entry that’s being processed by the from method. The from method will iterate through the whole iterable object or array to map each entry to a new value. The second parameter is the index of the array or iterable that’s being processed. It returns a new array with the new entry

For example, we can replace the map method with the Array.from method with the following code:

const arr = [{
    food: 'apple',
    color: 'red'
  },
  {
    food: 'orange',
    color: 'orange'
  },
  {
    food: 'grape',
    color: 'purple'
  },
  {
    food: 'banana',
    color: 'yellow'
  }
];
const foodColors = Array.from(arr, ({
  color
}) => color);
console.log(foodColors);

In the code above, we used the callback function that we passed into the Array.from method to get the value of the color field and put it in a new array. In the map method, we passed in a callback function with the first parameter, with the objects in the arr array destructured into color variable. The color variable is retrieved within the parameter, then we returned it. This will get us the value of the color field of each entry into the new foodColors array.

There are a few ways to replace specific values from an array. We can use the indexOf method to get the first occurrence of an array and then use the index to assign a new value to the entry in that array index. We can also use the splice method to remove the existing entry given the index, and then add another element to the same position given the same index. If we want to map each entry of an array to a new value, we can do it in a few ways. We can either use the map method or the Array.from method do to this.