mydatahack / javascript-workout

JavaScript workout

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

JavaScript Workout πŸ’ͺ🏼

This is a collection of short JavaScript programming solutions that you will encounter everyday.

As with any programming language, JavaScript has its own way of solving problems. Knowing how to do basic data type conversion or array manipulations will make you deliver your solutions faster 😊.

I hope this helps you to be a more efficient JavaScript developer πŸ€ŸπŸ’€πŸ€Ÿ.

Feel free to reach out to me πŸ€™!

Blog || Open Source Project || Band Camp

If you want to make a suggestion or contribute to this, feel free to pull the repo and make a pull request!


Topics 🏷

(1) Array of objects

(2) Array

(3) String format

(4) Spread Syntax

(5) Rest Parameter Syntax

(6) Trivia



(1) ARRAY OF OBJECTS


1. Create an array of number from an array of an object and do calculation

input

const input = [
  { name: "name A", score: 2 },
  { name: "name B", score: 1 },
  { name: "name C", score: 4 },
  { name: "name D", score: 5 },
];

output

# (1) Create an array
[2, 1, 4, 5]

# (2) Return Sum of the array
12

# (3) Return Max
5
Answer

Array.prototype.map will create an array of the value from the selected key in the JSON object.

Array.prototype.reduce will accumulate the number. The first argument is the accumulator function and second argument is the starting value.

Function.prototype.apply takes this value as a first argument and an array as a second argument. It will apply the function to the array. For example, Math.sum.apply(null, [1, 2, 3]) will sum up all the numbers in the array. Math.sum works with Math.sum(1, 2, 3). But, to make it work with an array, we need to use apply function.

# (1)
input.map(x => x.score);
// alternatively we can map array without .map()
// Array.from takes mapFunction as a second argument, which will be called on every element of an array.
Array.from(input, ({score}) => score);

# (2)
imput.map(x => x.score).reduce((a, b) => a + b, 0);

# (3)
Math.max.apply(null, input.map(x => x.score));
// or use spread operator
Math.max(...input.map(x => x.score));

2. Getting max datetime from a string from the object array

input

const input = [
  { datetime: "2020-04-29T03:23:48Z", spend: 300.0 },
  { datetime: "2020-06-03T23:26:43Z", spend: 300.0 },
  { datetime: "2020-05-30T17:28:14Z", spend: 300.0 },
  { datetime: "2020-06-27T18:21:07Z", spend: 300.0 },
];

output - return it as a Date object in the local time

Sun Jun 28 2020 04:21:07 GMT+1000
Answer

We can convert the string into a local time with new Date(). Then use the technique from question 1 to create an datetime array and apply max.

new Date(
  Math.max.apply(
    null,
    input.map((x) => new Date(x.datetime))
  )
);

// or use spread
new Date(Math.max(...input.map((x) => new Date(x.datetime))));

3. Adding new key-value pair in all the objects in an array

You have an array of an object with name. Can you add an unique id to all the objects?

input

const input = [{ name: "John" }, { name: "Tyson" }, { name: "Joan" }];

output

[
  { name: "John", id: 1 },
  { name: "Tyson", id: 2 },
  { name: "Joan", id: 3 },
];
Answer

We can use the map and use the index to add an unique id that increments.

input.map((data, index) => ({ name: data.name, id: index + 1 }));

4. Sorting object array by a key

input

const input = [
  { name: "John", score: "432" },
  { name: "Joe", score: "125" },
  { name: "Zoe", score: "320" },
  { name: "Ziggy", score: "532" },
  { name: "Dave", score: "211" },
  { name: "Sarah", score: "621" },
];

output - sort it in descending order

0: {name: "Sarah", score: "621"}
1: {name: "Ziggy", score: "532"}
2: {name: "John", score: "432"}
3: {name: "Zoe", score: "320"}
4: {name: "Dave", score: "211"}
5: {name: "Joe", score: "125"}
Answer Array.prototype.sort takes a callback function as a sorter. We can write a simple call back function and pass it.
const sorter = (key) => {
  return (a, b) => {
    if (a[key] > b[key]) {
      return -1;
    } else if (a[key] < b[key]) {
      return 1;
    } else {
      return 0;
    }
  };
};

input.sort(sorter("score"));

The above solution is too convoluted if we just want to sort by score. We can do this. This however does not work with name because they are string values.

// Ascending
input.sort((a, b) => a.score - b.score);

// Descending
input.sort((a, b) => b.score - a.score);

// This doesn't work...
input.sort((a, b) => b.name - a.name);

5. Sorting object array by multiple keys

We sorted an object array by a key in the previous question. What if the score is tie and want to sort it by the second key, name.

input

const input = [
  { name: "John", score: "432" },
  { name: "Joe", score: "125" },
  { name: "Zoe", score: "320" },
  { name: "Ziggy", score: "532" },
  { name: "Dave", score: "211" },
  { name: "Sarah", score: "621" },
  { name: "Alex", score: "320" },
];

output - see when the sore is the same, it's sorted by name.

0: {name: "Sarah", score: "621"}
1: {name: "Ziggy", score: "532"}
2: {name: "John", score: "432"}
3: {name: "Alex", score: "320"}
4: {name: "Zoe", score: "320"}
5: {name: "Dave", score: "211"}
6: {name: "Joe", score: "125"}
Answer

Apply the same method for the previous question. When the first key is the same, we can add another logic to sort it by the second key. Reference here

function rankingSorter(firstKey, secondKey) {
  return function (a, b) {
    if (a[firstKey] > b[firstKey]) {
      return -1;
    } else if (a[firstKey] < b[firstKey]) {
      return 1;
    } else {
      if (a[secondKey] > b[secondKey]) {
        return 1;
      } else if (a[secondKey] < b[secondKey]) {
        return -1;
      } else {
        return 0;
      }
    }
  };
}

input.sort(rankingSorter("score", "name"));

6. Sorting object array by datetime

Sort array by datetime.

input

const arrays = [
  { datetime: "2020-04-29T03:23:48Z", spend: 300.0 },
  { datetime: "2020-06-03T23:26:43Z", spend: 300.0 },
  { datetime: "2021-05-30T17:28:14Z", spend: 300.0 },
  { datetime: "2020-06-27T18:21:07Z", spend: 300.0 },
];

output - return the latest datetime record

{datetime: '2021-05-30T17:28:14Z', spend: 300.00}
Answer

Use custom function for sort. getTime() will convert datetime to a number of milliseconds since midnight Jan 1, 1970. We can also use localeCompare() method which returns a number indicating whether a reference string comes before, or after, or is the same as the given string in sort order.

arrays.sort((a, b)
  => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()
)[0]

// Using localeCompare()
arrays.sort(
function(a,b){
  return b.datetime.localeCompare(a.datetime)
})

7. Update object values

// input
const height = {
  john: 170,
  allen: 182,
  jack: 168,
};

// output
const output = {
  john: "170cm",
  allen: "182cm",
  jack: "168cm",
};
Answer

Use Object.entries to create an array of the key-value pairs. Object.entries(height) will create [['john', 170], ...]. Then, we can destructure the nested arrays with [k, v] in the map method.

const output = Object.entries(height).map(([k, v]) => ({ [k]: `${v}cm` }));

8. Aggregate by year

Aggregate the percentage by key.

input

const breakdown = [
  {
    percentage: 80,
    key: 2011,
  },
  {
    percentage: 10,
    key: 2010,
  },
  {
    percentage: 5,
    key: 2011,
  },
  {
    percentage: 5,
    key: 2010,
  },
  {
    percentage: 5,
    key: 2011,
  },
];

output - return the sum of percentage by year.

[
  {
    percentage: 90,
    key: 2011,
  },
  {
    percentage: 15,
    key: 2010,
  },
];
Answer

Approach 1: We can sort the data in a descending order and then use reduce to aggregate.

const sorted = breakdown.sort((a, b) => a.key - b.key);

const aggregated = breakdown
  .sort((a, b) => b.key - a.key)
  .reduce((acc, next) => {
    const currentAccIndex = acc.length - 1;
    if (acc.length && acc[currentAccIndex].key === next.key) {
      acc[currentAccIndex].percentage + next.percentage;
    } else {
      acc[currentAccIndex + 1] = next;
    }
    return acc;
  }, []);

Approach 2: This feels more like a JS witchcraft, doesn't it? Using Object.entries, and then reduce to aggregate.

const result = Object.entries(
  breakdown.reduce(
    (aggregate, current) => ({
      ...aggregate,
      [current.key]:
        (aggregate[current.key] ? aggregate[current.key] : 0) +
        current.percentage,
    }),
    {}
  )
).map((aggregate) => ({
  year: aggregate[0],
  percentage: aggregate[1],
}));

console.log(`output from the 3rd way ${JSON.stringify(result)}`);

(2) ARRAY


1. Create an array with a sequence of number

output

[0, 1, 2, 3, 4];
Answer

We can use either spread operator or Array from() and key() for ES6✌

For a reference, knowing how to use the Set object is great. Interestingly, this is not supported by IE11. If you do Array.from(new Set([1, 2, 3])), you will get an empty array without an error. Use set polyfill for IE11 support.

[...Array(5).keys()];

Array.from(Array(5).keys());

Array.from(new Set([0, 1, 2, 3, 4]));

2. Removing duplicates from an array

Remove duplicate value from an array.

const arr = ["apple", "orange", "banana", "orange", "apple"];
Answer

The Set type is new in ES6. It's similar to array, but not quite. It contains no duplicate value (member values are unique). So first, we dedupe by converting array into a set object and then convert back to an array with Array.from or spread operator.

Array.from(new Set(arr));

// or
[...new Set(arr)];

3. Replace the specific value

Replace watermelon with blueberry in the array below.

input

const fruits = ["apple", "banana", "watermelon", "melon"];

output

["apple", "banana", "blueberry", "melon"];
Answer

Use map with if condition.

const newFruits = fruits.map((x) => {
  if (x === "watermelon") return "blueberry";
  return x;
});

4. modulo operation

Create an array of the reminder after dividing each by 3. Let's try map the value without using map(). What can you do?

input

const input = [3, 4, 5, 6, 7];

output

[0, 1, 2, 0, 1];
Answer

We can map array without using .map(). Array.from takes arrayLike object as an first argument and map function applied to every element of the array as a second argument. Third argument is the value to use as this when executing map function. Second and third arguments are optional.

Array.from(input, (x) => x % 3);

5. Empty an array

Empty the array below:

input

const arr = [1, 2, 3, 4];

output

[];
Answer

We can just set the array length to 0. That's it 🀯

arr.length = 0;

6. Fill an empty array

Create an array of 10 1s as below.

output

[1, 1, 1, 1, 1, 1, 1, 1, 1, 1];
Answer

We can use fill() to fill an empty array with the value.

new Array(10).fill(1);

7. Find common value from two arrays

Find the common value from two arrays.

input

const numOne = [0, 2, 4, 6, 8, 8];
const numTwo = [1, 2, 3, 4, 5, 6];

output

[2, 4, 6];
Answer

First, we need de-duplicate the first array and use filter to find the common value.

[...new Set(numOne)].filter((x) => numTwo.includes(x));

8. Get random value from an array

Return random value from the color array below

const colors = ["blue", "white", "green", "navy", "pink", "black", "brown"];
Answer

Math.random() gives a random number between 0 and one. Then, we multiply with length of the array and take the floor to generate random index.

colors[Math.floor(Math.random() * colors.length)];

9. Get the last index of the value that occurs

Get the last index of 5 occurs in the array below. You need to return 9.

const numbers = [1, 5, 2, 6, 3, 5, 2, 3, 6, 5, 2, 7];
Answer

Here is the interesting method that javascript has. lastIndexOf().

numbers.lastIndexOf(5);

9. Get the last element of an array

Get the last element of the array

const array = [1, 2, 3, 4];

output

4
Answer
array.slice(-1)[0];

array.slice(-1).pop;

array[array.length - 1];

10. Convert array to string

const language = ["Japanese", "Spanish", "English", "German"];

From the array above, return string representing the elements of the list.

output

# (1)
Japanese, Spanish, English, and German

# (2)
Japanese, Spanish, English, or German

# (3)
Japanese Spanish English German
Answer

Intl internationalization API has ListFormat object. Intl.ListFormat is supported in all major browsers except IE11.

// (1)
const listFormatter = new Intl.ListFormat("en", {
  style: "long",
  type: "conjunction",
});

console.log(listFormatter.format(language));

typeof listFormatter.format(language); // -> string

// (2)
const listFormatter = new Intl.ListFormat("en", {
  style: "short",
  type: "disjunction",
});

// (3)
const listFormatter = new Intl.ListFormat("en", {
  style: "narrow",
  type: "unit",
});

(3) STRING FORMAT


1. Currency Format

input

const amount = 2398622.26;

output

"$2,398,622.26";
Answer

By using toLocaleString(), we can format currency with one line 🀯.

amount.toLocaleString("en-US", {
  style: "currency",
  currency: "USD",
});

We can natively format JavaScript Numbers.

formatting-number-cheatsheet

If you want to do this without native API, it gets really intense...

const formatAmount = (amount) => {
  const splitAmount = amount.split(".");
  const dollar = splitAmount[0];
  const decimal = splitAmount[1];
  const index = dollar.length / 3;
  const dollarArray = [];
  for (let i = 1; i <= index + 1; i++) {
    const startIndex = dollar.length - i * 2 - 1 - (i - 1);
    const finalStartIndex = startIndex < 0 ? 0 : startIndex;
    dollarArray.push(dollar.substring(finalStartIndex, startIndex + 3));
  }
  return `$${dollarArray.reverse().join(",")}.${decimal}`;
};

2. Datetime formatting

Formatting the datetime string below into a local time.

input

"2020-06-28T23:59:01Z";

output - this is the local time (AEST for me)

"29/06/2020 09:59:01 AM";
Answer

Let's give it a go by using Intl.DateTimeFormat. This will give you '29/06/2020'.

new Intl.DateTimeFormat("en-AU").format(new Date("2020-06-28T23:59:01Z"));

Now, Intl.DateTimeFormat has options. Let's pass the options.

const options = {
  year: "numeric",
  month: "numeric",
  day: "numeric",
  hour: "numeric",
  minute: "numeric",
  second: "numeric",
  hour12: true,
  timeZone: "Australia/Sydney",
};

const formatted = new Intl.DateTimeFormat("en-AU", options).format(
  new Date("2020-06-28T23:59:01Z")
);

The above will give us the output of '29/06/2020, 9:59:01 am'. We need to format this.

formatted.toUpperCase().split(", ").join(" ");

That's it πŸ€™!

If you want to do this without native API, it gets long 🐒.

formatUtcToLocal(timestamp: string): string {
  const localTime = new Date(timestamp)
  const year = localTime.getFullYear()
  const month = this.formatSingleDigit(localTime.getMonth() + 1)
  const day = this.formatSingleDigit(localTime.getDate())
  const hour = this.formatSingleDigit(this.convertHour(localTime.getHours()))
  const minutes = this.formatSingleDigit(localTime.getMinutes())
  const seconds = this.formatSingleDigit(localTime.getSeconds())
  const amOrPm = localTime.getHours() > 12 ? 'PM' : 'AM'

  return `${day}/${month}/${year} ${hour}:${minutes}:${seconds} ${amOrPm}`
}

formatSingleDigit(value: number): string {
  const formattedMonth = `0${value}`
  return formattedMonth.substring(formattedMonth.length - 2, formattedMonth.length)
}

convertHour(hour: number): number {
  if (hour > 12) {
      return hour - 12
  }
  return hour
}

(4) SPREAD SYNTAX


Spread syntax is cool πŸ₯³. Use spread syntax for all the questions. Let's build spread syntax muscle memory 🐣🐣🐣!

1. Spread with arrays

input

const fruits = ["apple", "banana", "blueberry"];
const vegs = ["lettuce", "tomato"];

Without spread, we would use .concat() to combine two arrays. Use spread syntax to .concat() two arrays.

output

["apple", "banana", "blueberry", "lettuce", "tomato"];
Answer
const combined = [...fruits, ...vegis];

// it is the same as
const combined = fruits.concat(vegis);

2. Add object to an array

Create a new object with a new fruit added and preserve original fruits array the same.

input

const fruits = [
  { id: 1, item: "apple" },
  { id: 2, item: "orange" },
];
const newFruit = { id: 3, item: "banana" };

output

// Create a new object called newFruits
[
  { id: 1, item: "apple" },
  { id: 2, item: "orange" },
  { id: 3, item: "banana" },
];
Answer
const newFruits = [...fruits, newFruit];

If you use Array.push() as below, it will modify the original array fruits. With spreading, we can preserve the original array.

fruits.push(newFruit);

3. Create an array from a set

We can use spread syntax to create an iterable array from a set.

input

const fruitSet = new Set();
set.add("apple");
set.add("orange");
set.add("banana");

output - create a new array called fruitArray

["apple", "orange", "banana"];
Answer

Using spread syntax with a set object will create an array.

[...fruitSet];

Set is a collection of unique values (either primitive or object).

const fruitSet = new Set();
set.add("apple");
set.add("apple");
set.add("banana");
// Set only contains "apple" and "banana"

We can actually create an array with unique values with Set and spread syntax.

[...new Set(["apple", "banana", "apple", "banana", "orange"])];
// results in ['apple', 'banana', 'orange']

4. Create an array from a string

We can also use spread to create an array from a string.

input

const str = "spread";

output

["s", "p", "e", "a", "d"];
Answer
const strArray = [...str];

5. Copying an object

We can spread an object to copy and update. It is the equivalent of Object.assign().

input

const original = { id: 1, fruit: "apple" };

output - create an copy of the original, copied.

Answer
const copied = { ...original };

This is the equivalent of

const copied = Object.assign({}, original);

6. Adding a new property on an existing object

Add a new property to an existing object in an immutable fashion.

input

const fruit = { id: 1, name: "apple" };

output

{ id: 1, name: 'apple', sweet: true }
Answer
const updatedFruit = { ...fruit, sweet: true };

We can do the spread if the added property is an object as below.

const add = { sweet: true }
const updatedFruit = { ...fruit, ...add }
// Then this will create the object with a new property
{ id: 1, name: 'apple', sweet: true }

7. Updating a property on an existing object

Update an existing property to create a new object in an immutable fashion.

input

const fruit = { id: 1, name: "apple", taste: "good" };

Update two properties with spread syntax. output

{ id: 1, name: 'banana', taste: 'great' }
Answer

To update multiple properties, we can just add them as below.

const updatedFruit = { ...fruit, name: "banana", taste: "great" };

Note that the order of the properties does not matter. We can do it like { ...fruit, taste: 'sweet', id: 4 } and it updates the correct property as long as the name matches.

We can update the property of the object from an object with spread, too!

const update = { name: 'banana', taste: 'great' }
const updatedFruit = { ...fruit, ...update }
// this will update the property
{ id: 1, name: 'banana', taste: 'great' }

8. Spread with nested object

Spread with nested object gets hairy. See if you can add a new property to the nested object as below.

input

const fruit = {
  id: 1,
  item: {
    name: "apple",
    sweet: true,
  },
};

output - add an price property to item

{
  id: 1,
  item: {
    name: 'apple',
    sweet: true,
    price: 1.0
  }
}
Answer

Nested objects need to be spread. In another word, we can spread the inner object, item, to retain the existing property.

const newFruit = { ...fruit, item: { ...fruit.item, price: 1.0 } };

9. More spread with nested objects

Use spread to update item.price.amount to 2.0.

input

const fruit = {
  id: 1,
  item: {
    name: "apple",
    sweet: true,
    price: {
      currency: "US",
      amount: 1.0,
    },
  },
};

output - add an price property to item

{
  id: 1,
  item: {
    name: 'apple',
    sweet: true,
    price: {
      currency: 'US',
      amount: 2.0
    }
  }
}
Answer
const updated = {
  ...fruit,
  item: {
    ...fruit.item,
    price: {
      ...fruit.item.price,
      amount: 2.0,
    },
  },
};

10. Spread function call

Spread can be used in a function call. We have a function called addAll. This will take 3 parameters. If we have an array of 3 numbers, how can we use the function?

const addAll = (a, b, c) => a + b + c;

// use addAll function on the array below
const input = [1, 2, 3];
Answer

This is a cool use case. We can in fact pass the spread input.

addAll(...input);

This is the same as using apply(). But, spread makes it shorter.

addAll.apply(null, input);

11. Convert array to an object

Another interesting use case for spread. Convert the array to an object as below.

input

const arr = ["1", "2", "3"];

output

{ 0: '1', 1: '2', 2: '3' }
Answer

It's the quick and dirty way to convert an array to an object with spread✌

{ ...arr }

(5) REST PARAMETER SYNTAX


In JavaScript, three dot syntax is interesting. It can be either spread or rest parameter syntax and they do exactly the opposite 🀯. Let's test your knowledge on rest parameter syntax πŸš€.

1. ...args

What is the output of below?

function check(...args) {
  console.log(args);
}

check(1, 2, 3, 4);
Answer

Rest parameter syntax will create an array instead of unpacking an array of object into individual values as in spread syntax.

The output will become an array of numbers 🀯.

[1, 2, 3, 4];

2. ...args with other arguments

What is the output of below?

function check(firstNum, secondNum, ...args) {
  console.log(firstNum);
  console.log(secondNum);
  console.log(args);
}

check(1, 2, 3, 4, 5);
Answer

Rest parameter syntax will create an array instead of unpacking an array of object into individual values as in spread syntax.

The output will become an array of numbers 🀯.

1;
(2)[(3, 4, 5)];

3. Destructing an array

What is the output of below code?

const [first, ...rest] = ["apple", "banana", "grape"];

console.log(first);
console.log(rest);
Answer

Rest parameter can be used when destructing arrays. ...rest will creates the shorter array.

apple[("banana", "grape")];

4. Destructing an object What is the output of below code?

const { id, ...rest } = { id: 1, name: "apple", price: 1.0 };

console.log(id);
console.log(rest);
Answer

Rest parameter can be used when destructing objects.

1
{ name: 'apple', price: 1.0 }

(6) Trivia

1. Truthy or Falsy?

Which one is evaluated as truthy?

  1. the number 0
  2. the BigInt 0n
  3. the keyword null
  4. the keyword undefined
  5. the boolean false
  6. the number NaN
  7. empty string ''
  8. empty array []
  9. empty object {}
Answer

Empty array [] and empty object {} are evaluated as truthy.

Any primitive type evaluate to true in JavaScript, with the exception of 1 to 7 🀯.


2. What is !!?

Answer

Returns an associated boolean value. True or false according to whether it is truthy or falsy values.

const number = 12;
!!number; // returns true


REFERENCES


There is a great JavaScript questions to get to know the language better. Your JavaScript knowledge will skyrocket πŸš€. Check out javascript-questions

I subscribe to JavaScript Weekly. It's a weekly email informing you on what is happening on JS landscape as well as useful JS tips! Highly recommended.

There are many code challenges websites. My recommendation is edatbit.com. If you are comfortable with JavaScript, go to the expert level. These interesting bite-size challenges will be a holiday for your mind 🌴.

You can get to build framework and library free JS apps from JavaScript30.com. It's free.

I am copy and pasting emoji from this website πŸ₯°.

About

JavaScript workout