The source code for this blog is available on GitHub.
Note
Top

Utility Functions: A Comprehensive Guide

Cover Image for Utility Functions: A Comprehensive Guide
Chen Han
Chen Han

Utility Components

Countdown

import React, { useState, useEffect } from "react";

const Countdown = ({ targetDate }) => {
  const calculateTimeLeft = () => {
    const difference = +new Date(targetDate) - +new Date();
    let timeLeft = {};

    if (difference > 0) {
      timeLeft = {
        days: Math.floor(difference / (1000 * 60 * 60 * 24)),
        hours: Math.floor((difference / (1000 * 60 * 60)) % 24),
        minutes: Math.floor((difference / 1000 / 60) % 60),
        seconds: Math.floor((difference / 1000) % 60),
      };
    }

    return timeLeft;
  };

  const [timeLeft, setTimeLeft] = useState(calculateTimeLeft());

  useEffect(() => {
    const timerId = setInterval(() => {
      setTimeLeft(calculateTimeLeft());
    }, 1000);

    // Clear the interval once the component is unmounted or the countdown is over
    return () => clearInterval(timerId);
  }, []);

  return (
    <div>
      {timeLeft.days ? <span>{timeLeft.days} days, </span> : null}
      {timeLeft.hours ? <span>{timeLeft.hours} hours, </span> : null}
      {timeLeft.minutes ? <span>{timeLeft.minutes} minutes, </span> : null}
      {timeLeft.seconds ? <span>{timeLeft.seconds} seconds</span> : null}
      {!timeLeft.days &&
      !timeLeft.hours &&
      !timeLeft.minutes &&
      !timeLeft.seconds ? (
        <span>Product is now available!</span>
      ) : null}
    </div>
  );
};

export default function CountDownExample() {
  // Set the target date to December 31, 2023, 23:59:59 for demonstration purposes.
  const targetDate = "2024-07-26T23:59:20.000Z";

  return (
    <div style={{ textAlign: 'center', marginTop: '50px' }}>
      <h1>Product Launch Countdown</h1>
      <Countdown targetDate={targetDate} />
    </div>
  );
}

Certainly! Let's break down the code and understand how setInterval, useEffect, and useState work together in the provided Countdown component:

useState

const [timeLeft, setTimeLeft] = useState(calculateTimeLeft());

useState is a React hook that allows function components to "hold" state. In this case, the timeLeft state holds the calculated difference between the target date and the current date.

  • timeLeft: The current state value.
  • setTimeLeft: A function to update the state value.

useEffect

useEffect(() => {
    const timerId = setInterval(() => {
      setTimeLeft(calculateTimeLeft());
    }, 1000);

    // Clear the interval once the component is unmounted or the countdown is over
    return () => clearInterval(timerId);
}, []);

useEffect is another React hook. It allows you to perform side effects (like DOM updates, data fetching, setting up subscriptions, etc.) in function components.

  1. The function passed to useEffect will run after the component renders.
  2. The second argument to useEffect is a dependency array. If this array is empty (like it is in the code you provided), the effect will run once after the initial render and never again. This is equivalent to componentDidMount in class components.
  3. The function returned from within the effect (return () => clearInterval(timerId);) is a cleanup function. This will be executed if the component is unmounted or if the dependencies in the dependency array change (but in this case, it'll only run on unmount since the dependency array is empty). This is useful for cleaning up any ongoing processes like intervals, event listeners, etc.

setInterval

Within the useEffect, we have:

const timerId = setInterval(() => {
    setTimeLeft(calculateTimeLeft());
}, 1000);

setInterval is a native JavaScript method that will execute the provided function every 1000 milliseconds (1 second, in this case). Every second, the calculateTimeLeft function is called, and the new value is set to the timeLeft state using the setTimeLeft function.

When the timeLeft state is updated, the component re-renders, and the new values are displayed to the user.

Why is this combination powerful?

  1. State Management: Using useState, we can manage and display dynamic data (like a countdown) in a functional component.
  2. Side Effects: useEffect allows us to set up side effects (like an interval) that can use and modify the state. It also provides a mechanism for cleanup, ensuring that we don't end up with dangling intervals when the component is no longer in use.
  3. Dynamic Updates: With setInterval, we can create dynamic updates to our component, triggering state updates at fixed intervals.

Together, they provide a way to create dynamic, stateful logic in a functional component.

Utility Function

Removing empty values

Here are some concise ways to remove empty values from an array in JavaScript:

const arr = [1,2,,3,,-3,null,,0,,undefined,4,,4,,5,,6,,,,];

// This will remove any values that are falsy (e.g. 0, null, undefined, etc.)
arr.filter(n => n)
// [1, 2, 3, -3, 4, 4, 5, 6]

// This will remove any values that are not numbers
arr.filter(Number)
// [1, 2, 3, -3, 4, 4, 5, 6]

// This will remove any values that are not truthy (e.g. true, non-empty strings, etc.)
arr.filter(Boolean)
// [1, 2, 3, -3, 4, 4, 5, 6]

In the above example, the original array arr contains a mix of numbers and empty values. The filter method is used to create a new array that only contains the non-empty values. Each of the three methods shown use a different callback function to determine which values should be included in the new array.

The first method uses a callback that checks if the value is truthy, the second method uses a callback that checks if the value is a number, and the third method uses a callback that checks if the value is truthy.

Check Empty Object

function isObjectEmpty(value) {
  return (
    Object.prototype.toString.call(value) === '[object Object]' &&
    JSON.stringify(value) === '{}'
  );
}

isObjectEmpty({});           // true ✅
isObjectEmpty(new Object()); // true ✅
// Native
export const isEmpty = (obj) =>
  [Object, Array].includes((obj || {}).constructor) &&
  !Object.entries(obj || {}).length;

/* -------------------------
  Plain JS for Newer Browser
----------------------------*/
Object.keys(empty).length === 0 && empty.constructor === Object

/* Lodash: true */
_.isEmpty({});

/* Underscore: true */
_.isEmpty({});

/* JQuery: true */
jQuery.isEmptyObject({});

Merging multiple arrays

This function creates a new array of elements, grouped based on their position in the original arrays. It uses the Math.max() function to find the longest array in the arguments, and then uses the Array.from() method to create an array with that length. The mapping function within Array.from() creates an array of grouped elements. If the lengths of the argument arrays vary, undefined is used where no value could be found.

/*=== Merge multiple arrays ===*/

export const zip = (...arrays) => {
  const maxLength = Math.max(...arrays.map(x => x.length));
  
  return Array.from({ length: maxLength }).map((_, i) => {
    return Array.from({ length: arrays.length }, (_, k) => arrays[k][i]);
  });
};

Creates an array of elements, grouped based on their position in the original arrays. (link)

  • Use Math.max(), Function.prototype.apply() to get the longest array in the arguments.
  • Create an array with that length as return value and use Array.from() with a mapping function to create an array of grouped elements.
  • If lengths of the argument arrays vary, undefined is used where no value could be found.
/*=== Merge multiple arrays and process with a function ===*/

export const zipWith = (...array) => {
  const fn =
    typeof array[array.length - 1] === 'function' ? array.pop() : undefined;
  return Array.from({ length: Math.max(...array.map(a => a.length)) }, (_, i) =>
    fn ? fn(...array.map(a => a[i])) : array.map(a => a[i])
  );
};

Creates an array of elements, grouped based on the position in the original arrays and using a function to specify how grouped values should be combined. (link)

  • Check if the last argument provided is a function.
  • Use Math.max() to get the longest array in the arguments.
  • Use Array.from() to create an array with appropriate length and a mapping function to create array of grouped elements.
  • If lengths of the argument arrays vary, undefined is used where no value could be found.
  • The function is invoked with the elements of each group.

Here is an example of how to use zip and zipWith with the provided JavaScript functions:

// Example usage of zip
const array1 = [1, 2, 3];
const array2 = [4, 5, 6];

const zippedArrays = zip(array1, array2);
// zippedArrays is [[1, 4], [2, 5], [3, 6]]

// Example usage of zipWith
const array3 = [1, 2, 3];
const array4 = [4, 5, 6];

const zippedArraysWith = zipWith((a, b) => a + b, array3, array4);
// zippedArraysWith is [5, 7, 9]

In the first example, zip is used to combine the elements of array1 and array2 into an array of pairs. The resulting array is [[1, 4], [2, 5], [3, 6]].

In the second example, zipWith is used to combine the elements of array3 and array4 using the provided function (a, b) => a + b, which adds the elements together. The resulting array is [5, 7, 9].

Object to Comma Separated List

If the data type of the Object is an array, it is converted to a list using commas. Here is an explanation and example of how you can convert an object with array-like properties to a string using a comma as a delimiter:

The following result was inspired:

// array like object
const obj = { a: 1, b: 2, c: 3 };
console.log(Object.entries(obj)); // [ ['a', 1], ['b', 2], ['c', 3] ]

You can use reduce:

// convert array to comma separated list
q = Object.entries(q).reduce((acc, cur) => {
  return {...acc, [cur[0]]: (Array.isArray(cur[1]) ? cur[1].toString() : cur[1]) }
}, {})

console.log(result); // { a: '1,2,3' }

Splitting into a two-dimensional array

To split the data into a two-dimensional array using customer[][cond], the following data can be transformed into the following form using reduce:

[
  {name: 'customer[][cond]', value: 'filterAccuPrice'},
  {name: 'customer[][operator][]', value: '>'},
  {name: 'customer[][accuPrice][]', value: ''},
  {name: 'customer[][cond]', value: 'filterAccuPrice'},
  {name: 'customer[][operator][]', value: '>'},
  {name: 'customer[][accuPrice][]', value: ''}
]

to:

[
  ['filterAccuPrice', '>', ''],
  ['filterAccuPrice', '>', ''],
]

This can be done with the following code:

let formattedData = []

formattedData = $("form#rf").serializeArray()?.reduce((accu, curr) => {
  const _l = accu.length
  if(curr.name === "customer[][cond]") {
   return [...accu, [curr.value]]
  } else {
   return [...accu.splice(0, _l - 1), accu.concat(curr.value)]
  }
}, [])

This code is using the reduce method to iterate over an array of objects and transform it into a two-dimensional array. The serializeArray method is used to create an array of objects from a form element, where each object represents an input field in the form.

Here are some common methods provided by the jQuery.fn.form function:

  • serialize(): This method creates a URL encoded text string by serializing form values.
  • serializeArray(): This method creates an array of objects with name and value properties, by serializing form values.
  • submit(): This method submits a form. It can be used to submit a form programmatically, or to bind it to a submit event handler.
  • clearForm(): This method clears all form elements, including text and hidden input elements, drop-down lists, and checkboxes.
  • resetForm(): This method resets a form to its default state. It can be used to reset a form programmatically, or to bind it to a reset event handler.

There are also some additional methods available, such as ajaxSubmit(), ajaxForm(), and validate(), which can be used to submit a form asynchronously, validate form elements, and more.

The reduce method takes in an initial value (in this case, an empty array) and a callback function that is called for each element in the array. The callback function takes in two arguments, accu and curr, which represent the accumulator and the current element of the array, respectively.

Inside the callback function, a variable _l is defined as the length of the accumulator array. If the name property of the current element is equal to "customer[][cond]", a new array is created with the value of the value property of the current element and is added to the accumulator array using the spread operator (...).

If the name property of the current element is not equal to "customer[][cond]", the accumulator array is modified by removing the last element and concatenating the value property of the current element to the accumulator array.

At the end of the reduce method, the modified accumulator array is returned and stored in the formattedData variable.

Filter empty values

using the filter method to remove falsy values from an array. The filter method creates a new array with all elements that pass the test implemented by the provided function. In this case, the function x => !!x is used as the test.

The expression !!x evaluates to the boolean value true if x is truthy, and false if x is falsy. The !! operator is equivalent to casting the value of x to a boolean type.

For example, the value 1 is truthy, so !!1 evaluates to true, and the value 0 is falsy, so !!0 evaluates to false.

By using !!x as the test in the filter method, the following values will be removed from the array:

  • null
  • undefined
  • NaN
  • 0
  • "" (empty string)
[1,"", null, NaN, 2, undefined,4,5,6].filter(x => !!x); //returns [1, 2, 4, 5, 6]

After running filter, the resulting array will contain only the truthy values: [1, 2, 4, 5, 6].

Shuffle an Array

function getMultipleRandom(arr, num) {
  const shuffled = [...arr].sort(() => 0.5 - Math.random());

  return shuffled.slice(0, num);
}

The getMultipleRandom function takes in an array arr and a number num, and returns a new array containing num elements randomly chosen from the original array.

Here's how it works:

  1. A new array shuffled is created using the spread operator (...). This creates a shallow copy of the original array arr.

  2. The shuffled array is then sorted randomly using the sort() method and a callback function that always returns 0.5 minus a random number (either 0 or 1). This effectively shuffles the elements in the array.

    The Math.random() function returns a random number between 0 (inclusive) and 1 (exclusive). By subtracting it from 0.5, we get a number that is either less than or greater than 0.5. When we pass this function to the sort method of an array, it uses the returned value to determine how to sort the elements of the array. If the returned value is less than 0, the element being compared is moved to an earlier index in the array. If the returned value is greater than 0, the element being compared is moved to a later index in the array.

    By using () => 0.5 - Math.random(), we are effectively shuffling the elements of the array because the returned value is random and therefore the elements are being placed in the array in a random order.

    The sort() function in JavaScript takes an optional compare function as an argument, which determines the order of the elements in the resulting sorted array. The compare function should return a negative, zero, or positive value, depending on the relationship between the two elements being compared.

    In this case, the compare function is an anonymous function that always returns a random value between -0.5 and 0.5. When the sort() function uses this compare function, it will randomly rearrange the elements in the array.

  3. Finally, the slice() method is used to select the first num elements from the shuffled array and return them as a new array.

For example, if you called getMultipleRandom([1, 2, 3, 4, 5], 3), you might get back [2, 4, 5], or [1, 3, 4], or any other combination of 3 elements randomly chosen from the original array.

Omit Object Properties

Explain the difference between the following two functions

export const omitProperties = (...props) => o => Object.fromEntries(Object.entries(o).filter((key) => !props.some(p => key.includes(p))));

export const omitProperties = (...props) => o => Object.fromEntries(Object.entries(o).filter(([key]) => !props.some(p => key.includes(p))));

The first omitProperties function uses destructuring to extract the key and value from the object entries array, but doesn't do anything with the value. The second omitProperties function also uses destructuring, but this time to extract only the key from the object entries array. Other than this difference, both functions are identical in their behavior.

In both functions, the object o is converted to an array of key-value pairs using Object.entries(o), and this array is then filtered using the Array.prototype.filter() method. The callback function for the filter() method checks whether any of the props passed to omit are included in the current key using the Array.prototype.some() method. If this is the case, the current key-value pair is excluded from the final result. Finally, the filtered array of key-value pairs is converted back to an object using Object.fromEntries().

const cc = Object.entries({a: 1, b: 2, c: 3}).map(([key]) => key)
console.log(cc) //=> ['a', 'b', 'c']

const dd = Object.entries({a: 1, b: 2, c: 3}).map((key) => key)
console.log(dd) //=> [['a', 1], ['b', 2], ['c', 3]]

const ee = Object.entries({a: 1, b: 2, c: 3}).map(([, value]) => value)
console.log(ee) //=> [1, 2, 3]

There are includes for either string and array

console.log("TaxRule".includes("Tax"))        // true
console.log(["TaxRule", 123].includes("Tax")) // false

In the first example, the string method includes is used on the string "TaxRule" and it returns true because the string "Tax" is found within "TaxRule".

In the second example, the includes method is used on an array and it returns false because the array does not contain the element "Tax". The includes method is only available on arrays, not on objects or other types.

If you want to check if an array contains a particular element, you can use the includes method or you can use the indexOf method, which returns the index of the element in the array if it exists, or -1 if it does not exist.

const arr = ["TaxRule", 123];
console.log(arr.includes("Tax"));  // false
console.log(arr.indexOf("Tax"));   // -1

You can also use the some method, which returns true if at least one element in the array satisfies the given test function, or false if no element satisfies the test.

For example:

const arr = ["TaxRule", 123];
console.log(arr.some(e => e.includes("Tax")));  // true

Pick Object Properties

Explain the difference between the following two functions

const getSelectedProperties = (...props) => o => Object.fromEntries(Object.entries(o).filter(([key]) => props.some(p => key.includes(p))));

This is a function that takes in a list of properties as arguments, and returns a new function that takes in an object. The returned function filters the entries of the object based on the list of properties passed as arguments to the outer function.

The inner function filters the entries of the object by checking if the key of each entry is included in the list of properties passed to the outer function. If the key is included, the entry is included in the filtered list. If not, it is excluded.

The filtered list is then used to create a new object using Object.fromEntries, which takes an array of key-value pairs and returns an object with those key-value pairs.

For example:

const getAandC = getSelectedProperties('a', 'c');
const obj = {a: 1, b: 2, c: 3};
const filteredObj = getAandC(obj);

console.log(filteredObj); // {a: 1, c: 3}

Reverse key and value

const reversedArray = (array) => array.reduce((acc, [key, value]) => {
  acc[value] = key;
  return acc;
}, {});

This function takes an array as an argument and returns an object that has the keys and values from the array reversed.

The function uses the reduce() method to iterate over the array and build up the object. The reduce() method takes a callback function and an initial value (in this case, an empty object). The callback function takes two arguments: an accumulator (acc) and the current element ([key, value]). The accumulator is initialized to the empty object and is updated on each iteration. The current element is destructured into its key and value components, which are then added to the accumulator with the value as the key and the key as the value. The accumulator is then returned, which becomes the final result.

Here's an example of how the function might be used:

const array = [  ['a', 1],
  ['b', 2],
  ['c', 3]
];

const reversed = reversedArray(array);
console.log(reversed); // { 1: 'a', 2: 'b', 3: 'c' }

Sort an array

This code exports a function called desc that takes an array arr and an optional string argument col (default value is "id"). The function returns a new sorted array with the same elements as arr, but sorted in descending order based on the values at the property specified by col.

const desc = (arr, col = "id") => arr.sort((a, b) => b[col] - a[col]);
const desc = (arr, col = "id") => [...arr].sort((a, b) => b[col] - a[col]);

For example, if you have an array of objects with properties "id" and "score":

const arr = [{id: 1, score: 100}, {id: 2, score: 50}, {id: 3, score: 75}];

You can sort this array in descending order by the "score" property like this:

const sortedArray = desc(arr, "score");
//=> [{id: 1, score: 100}, {id: 3, score: 75}, {id: 2, score: 50}]

The function first spreads the elements of arr into a new array using the spread operator (...arr), then uses the sort() method to sort the new array in place. The sort() method compares the values at the property specified by col for each element in the array, and rearranges the elements based on this comparison. The b[col] - a[col] part is what determines the sort order - it causes the elements to be sorted in descending order because the b value comes before the a value. If you wanted to sort the array in ascending order, you could use a[col] - b[col] instead.

Sum an array of numbers

These functions provide utility for dealing with numbers in JavaScript. The naNToZero function takes a number and returns it as a number type, or 0 if the value is not a number. The formatPrice function takes a number and returns it as a string with two decimal places. The addAllNumber function takes any number of arguments and returns their sum, casting any non-numeric values to 0. The addAllPrice function works similarly, but returns the sum as a string with two decimal places. These functions can be useful for ensuring that calculations with numbers are performed accurately and consistently.

/* cast NaN to 0 */
const naNToZero = (number) => (isNaN(+number) ? 0 : +number);
const formatPrice = (number) => naNToZero(parseFloat(number)).toFixed(2);
/**
 * addAllNumber("1", 2, 3, "45", "-", "1", "444444")
 * => 444496
 */
const addAllNumber = (...props) => props.reduce((a, b) => a + naNToZero(+b), 0);
/**
 * addAllPrice("1", 2, 3, "45", "-", "1", "444444")
 * => '444496.00'
 */
const addAllPrice = (...props) => parseFloat(addAllNumber(...props)).toFixed(2);

Here is a brief overview of the code provided:

  • naNToZero is a utility function that takes in a number and returns 0 if it is not a number (NaN). Otherwise, it returns the number.
  • formatPrice is a utility function that takes in a number and uses naNToZero to ensure that it is a valid number before formatting it to have 2 decimal places.
  • addAllNumber is a utility function that takes in a variable number of arguments and uses naNToZero to ensure that all of them are valid numbers before adding them up and returning the result.
  • addAllPrice is a utility function that takes in a variable number of arguments, adds them up using addAllNumber, and then formats the result as a price with 2 decimal places.

Overall, these utility functions seem useful for performing basic arithmetic operations and formatting numbers as prices. They may be particularly useful in a context where data may not always be in a clean, predictable format.

We can rename them as the following to make them readable.

const toNumber = (val) => (isNaN(+val) ? 0 : +val);
const formatPrice = (number) => toNumber(number).toFixed(2);
const addAll = (...props) => props.reduce((a, b) => a + toNumber(b), 0);
const addAllPrice = (...props) => formatPrice(addAll(...props));

===

const obj1 = {};
const obj2 = {};

console.log(JSON.stringify(obj1) === JSON.stringify(obj2)); // logs true

This will return true if both objects are empty and false if either object has any properties. Alternatively, you can use the Object.keys method to check if both objects have the same number of properties. For example:

console.log(Object.keys(obj1).length === Object.keys(obj2).length); // logs true

This will return true if both objects have the same number of properties, regardless of the actual property names and values.

Reducer

In JavaScript, the reduce function is a powerful and versatile tool that can be used to perform a wide range of operations on arrays. With reduce, you can do anything you want, from calculating sums and averages, to filtering and transforming data, to creating complex data structures. The reduce function works by applying a specific function to each element in an array, and accumulating the results into a single value. This allows you to take an array of data and transform it into any other form you desire. Whether you want to calculate statistics, transform data, or build complex data structures, the reduce function can do it all.

tally

The tally object is used in the code you provided to keep track of the combined and tallied data from the arrays of data that are passed into the addItUp function. The reduce method is used to loop over each item in the arrays and update the tally object with the values from each item. The tally object is initialized as an empty object and is passed as the second argument to the reduce method. It is also used as the accumulator in the reduce callback function, which means that it is updated with the new values for each person on each iteration of the reduce loop.

The tally object is used to store the combined and tallied data from the arrays of data because it allows the addItUp function to easily access and update the values for each person. It also provides a convenient way to return the combined and tallied data from the function, as it can be simply returned as-is at the end of the reduce loop. Overall, the tally object is an important part of the code because it allows the addItUp function to combine and tally the data from the arrays of data in a flexible and efficient manner.

/* eslint-disable */
const arr1 = [
  { name: 'Joe Brown', goals: 0, assists: 0, points: 0 },
  { name: 'Jim Bob', goals: 2, assists: 1, points: 3 },
  { name: 'Harry Styles', goals: 1, assists: 1, points: 2 },
  { name: 'Craig Mack', goals: 5, assists: 7, points: 12 },
  { name: 'Wés BÔS 🔥', goals: 5, assists: 7, points: 12 },
  { name: 'DOG', bones: 100000, goals: 5, assists: 7, points: 12 },
];
// prettier-ignore
const arr2 = [
  { name: 'Craig Mack', goals: 3, assists: 3, points: 6, ppg: 0, ppa: 0, pims: 0, },
  { name: 'Jim Bob', goals: 1, assists: 4, points: 5, ppg: 0, ppa: 1, pims: 0 },
  { name: 'Joe Brown', goals: 0, assists: 0, points: 0, ppg: 0, ppa: 0, pims: 0, },
  { name: 'Harry Styles', goals: 0, assists: 0, points: 0, ppg: 0, ppa: 0, pims: 0, },
];

function addItUp(...arraysOfData) {
  const data = arraysOfData.flat();
  const tally = data.reduce(function(tallyArray, item) {
    // first check if this person is new
    const { name, ...points } = item;
    console.log(`Working on ${name}`);
    console.log(points);
    tallyArray[name] = tallyArray[name] || {};
    // Loop over each of their properties and add them up
    Object.entries(points).forEach(([key, val]) => {
      if(tallyArray[name][key]) {
        // Already exists, so we increment the value by the next line
        tallyArray[name][key] = tallyArray[name][key] + val;
      } else {
        // brand new, set it to that value!
        tallyArray[name][key] = val;
      }
    })
    return tallyArray;
  }, {});
  return tally;
}

const result = addItUp(arr1, arr2);
console.table(result)

The addItUp function is a utility that takes in one or more arrays of data and combines them into a single object, tallying up the values for each property for each person. The function uses the reduce method to loop over each item in the arrays, and it checks whether the person represented by that item is already present in the tally object. If the person is new, the function adds them to the tally object and sets their properties to the corresponding values from the item. If the person is already present in the tally object, the function increments their properties by the corresponding values from the item.

The addItUp function first flattens the arrays of data into a single array using the flat method. Then, it uses the reduce method to loop over each item in the array and build up the tally object. Inside the reduce callback function, the tallyArray parameter represents the current state of the tally object, and the item parameter represents the current item being processed. The function destructures the name and points properties from the item object, and uses the name property to check whether the person represented by the item is already present in the tally object.

If the person is not present in the tally object, the function adds them to the tally object and sets their properties to the corresponding values from the points object. If the person is already present in the tally object, the function loops over the properties of the points object and increments their corresponding values in the tally object.

Finally, the addItUp function returns the tally object, which contains the combined and tallied data from all of the arrays of data that were passed in as arguments. The console.table method is then used to log the resulting tally object to the console in a tabular format.

I want to pick id in option_values as key, and the value is purchasing_disabled at the same layer in option_values. like {138: false}

To create an object that maps the id property of the option_values objects to the purchasing_disabled property at the same level, you can use the following code:

const arr = [{
  "id": 138,
  "product_id": 142,
  "sku": "CS-HYTE-Y40-BW",
  "sku_id": 157,
  "purchasing_disabled": false,
  "option_values": [{
    "id": 132,
    "label": "White",
    "option_id": 124,
    "option_display_name": "Color"
  }]
}, {
  "id": 139,
  "product_id": 142,
  "sku": "CS-HYTE-Y40-B",
  "sku_id": 158,
  "purchasing_disabled": false,
  "option_values": [{
    "id": 133,
    "label": "Black",
    "option_id": 124,
    "option_display_name": "Color"
  }]
}];

const obj = arr.reduce((acc, curr) => {
  acc[curr.option_values[0].id] = curr.purchasing_disabled;
  return acc;
}, {});

console.log(obj);  // Output: {138: false, 139: false}

This code uses the reduce() method to iterate over the array and create a new object with the desired structure. The acc argument represents the accumulator, which is initialized as an empty object and is used to store the transformed values. The curr argument represents the current element being processed in the array.

For each iteration, the code adds a new key-value pair to the accumulator object, using the id property of the first element in the option_values array as the key and the purchasing_disabled property as the value. The reduce() method returns the accumulator object when all elements have been processed.

Here is another thing. option_values can be more than one

If the option_values array can contain more than one element, you can modify the code to handle this case by using a loop to iterate over all the elements in the array and adding a key-value pair for each one.

const arr = [{
  "id": 138,
  "product_id": 142,
  "sku": "CS-HYTE-Y40-BW",
  "sku_id": 157,
  "purchasing_disabled": false,
  "option_values": [{
    "id": 132,
    "label": "White",
    "option_id": 124,
    "option_display_name": "Color"
  }, {
    "id": 135,
    "label": "Large",
    "option_id": 125,
    "option_display_name": "Size"
  }]
}, {
  "id": 139,
  "product_id": 142,
  "sku": "CS-HYTE-Y40-B",
  "sku_id": 158,
  "purchasing_disabled": false,
  "option_values": [{
    "id": 133,
    "label": "Black",
    "option_id": 124,
    "option_display_name": "Color"
  }, {
    "id": 136,
    "label": "Medium",
    "option_id": 125,
    "option_display_name": "Size"
  }]
}];

const obj = arr.reduce((acc, curr) => {
  curr.option_values.forEach(optionValue => {
    acc[optionValue.id] = curr.purchasing_disabled;
  });
  return acc;
}, {});

console.log(obj);  // Output: {132: false, 135: false, 133: false, 136: false}

This code uses a forEach() loop to iterate over all the elements in the option_values array

How do let the data structure as i expected in javascript

I want the the data from [{id: 1, enable: false}, {id:2, enable: true}] to be {1: false, 2: true}

To transform an array of objects with the structure {id: number, enable: boolean} into an object with the structure {number: boolean}, you can use the following code:

const arr = [{id: 1, enable: false}, {id:2, enable: true}];

const obj = arr.reduce((acc, curr) => {
  acc[curr.id] = curr.enable;
  return acc;
}, {});

console.log(obj);  // Output: {1: false, 2: true}

This code uses the reduce() method to iterate over the array and create a new object with the desired structure. The acc argument represents the accumulator, which is initialized as an empty object and is used to store the transformed values. The curr argument represents the current element being processed in the array.

For each iteration, the code adds a new key-value pair to the accumulator object, using the id property as the key and the enable property as the value. The reduce() method returns the accumulator object when all elements have been processed.

I hope this helps! Let me know if you have any questions.

Advanced utility functions

Advanced utility functions can be an essential part of your JavaScript toolkit, even though they may be slightly more difficult to understand and use. Some examples of advanced utility functions include debounce and throttle, which can be useful for limiting the rate at which a function is called or for improving the performance of your application. Other advanced utility functions might include functions for deep cloning objects, for creating and manipulating promises, or for working with asynchronous code. These functions can be challenging to learn and use, but they can greatly improve the functionality and efficiency of your code.

debounce

Debouncing is a technique that delays the execution of a function until a specified amount of time has passed without it being called. This can be useful for preventing a function from being called too frequently when it is being triggered by events that occur rapidly, such as a user typing or scrolling.

export function debounce(func, timeout = 1000) {
  let timer;
  return (...args) => {
    clearTimeout(timer);
    timer = setTimeout(() => {
      func.apply(this, args);
    }, timeout);
  };
}

This code defines a function called debounce that takes in a function func and an optional argument timeout with a default value of 1000.

It returns a new function that can be used in place of func. When this new function is called, it clears any existing timers that have been set with setTimeout and then sets a new timer using setTimeout. The timer will call the original function func after the specified timeout has passed.

This is useful for situations where you want to limit the rate at which a function is called. For example, you might use debouncing to prevent a function that sends an API request from being called too frequently, which could result in unnecessary load on the server.

The apply method is used to call the function func with the correct value of this and the arguments passed to the debounced function. The spread operator (...) is used to pass all of the arguments passed to the debounced function as individual arguments to func.

The following is an example of where i use debounce

const debouncedChangeHandler = useCallback(debounce(this.zipCodeChange, 800), []);

&lt;input
  name=&quot;zip_code&quot;
  ref={this.zipCodeRef}
  className=&quot;w-full block border p-2 my-2 rounded-lg&quot;
  placeholder=&quot;Zip code *&quot;
  onChange={debouncedChangeHandler}
/&gt;

In the example, debounce is used to wrap a function called zipCodeChange and set a debounce timeout of 800 milliseconds. The resulting debounced function is stored in the debouncedChangeHandler constant using the useCallback hook.

The debounce function in the example code takes in a function this.zipCodeChange and a timeout value of 800 milliseconds. It returns a new function that can be used in place of this.zipCodeChange. When this new function is called, it will clear any existing timers that have been set with setTimeout and then set a new timer using setTimeout. The timer will call the original function this.zipCodeChange after the specified timeout has passed.

useCallback is a hook in React that returns a memoized version of the callback function passed to it. The useCallback hook is used to ensure that the debouncedChangeHandler function is only created once and not recreated on every render. This can help to optimize the performance of the component by avoiding unnecessary function re-creation. It helps to improve the performance of your application by preventing the callback function from being re-created on every render. In the example code, debouncedChangeHandler is a memoized version of the debounced this.zipCodeChange function that is only re-created if the dependencies passed to useCallback change.

The debouncedChangeHandler function is then passed as the onChange event handler for the input element. This means that the this.zipCodeChange function will be debounced and will only be called after the user has stopped typing in the input field for 800 milliseconds. This can help to prevent the function from being called too frequently and potentially overwhelming the server with unnecessary requests. The debouncedChangeHandler function is then passed as the onChange prop for an input element. This means that whenever the value of the input changes, the debouncedChangeHandler function will be called. However, it will only actually call the zipCodeChange function after the specified debounce timeout of 800 milliseconds has passed. This helps to prevent the zipCodeChange function from being called too frequently and potentially causing unnecessary load on the server or other resources.

Throttle

Throttling is a technique that limits the number of times a function can be called over a given period of time. This can be useful for preventing a function from being called too frequently when it is being triggered by events that occur at a consistent rate, such as a user resizing a window or moving a mouse.

© 2024 WOOTHINK. All Rights Reserved.
Site MapTerms and ConditionsPrivacy PolicyCookie Policy