π JavaScript Utils: Part One
- Published on
Part One of the JavaScript Utils series where I share code snippets which can be directly used in your projects. All the snippets on this page are licensed under CC0-1.0, unless mentioned otherwise.
Get unique values in an array
const myArray = ['πΊ', 'πΊ', 'π·', 'π₯', 'π·', 'π₯€']
const unique = (array) => array.filter((val, index, arr) => arr.indexOf(val) === index)
// OR
const unique = (array) => Array.from(new Set(array))
// OR
const unique = (array) => [...new Set(numbers)]
console.log(unique(myArray)) // => [ "πΊ", "π·", "π₯", "π₯€" ]
Remove Empty elements from an Array
const input = [0, 1, null, 2, '', 3, undefined, 3, , , , , , 4, , 4, , 5, , 6, , , ,]
const clean = (array) => array.filter(Boolean)
console.log(clean(input))
The typical pattern that I see often used is to remove elements that are falsy, which include an empty string ""
, 0
, NaN
, null
, undefined
, and false
. Using the Boolean
constructor function, we can simply return values which are truthy.
Copy Text to Clipboard 1
function fallbackCopyTextToClipboard(text) {
const textArea = document.createElement('textarea')
textArea.value = text
// Avoid scrolling to bottom
textArea.style.top = '0'
textArea.style.left = '0'
textArea.style.position = 'fixed'
document.body.appendChild(textArea)
textArea.focus()
textArea.select()
try {
var successful = document.execCommand('copy')
var msg = successful ? 'successful' : 'unsuccessful'
console.log('Fallback: Copying text command was ' + msg)
} catch (err) {
console.error('Fallback: Oops, unable to copy', err)
}
document.body.removeChild(textArea)
}
function copyTextToClipboard(text) {
if (!navigator.clipboard) {
fallbackCopyTextToClipboard(text)
return
}
navigator.clipboard.writeText(text).then(
function () {
console.log('Async: Copying to clipboard was successful!')
},
function (err) {
console.error('Async: Could not copy text: ', err)
}
)
}
Bonus: Did you know you can use the copy
method in the Developer tools console to quickly copy results of operations? Try it out!
Default Function Parameters
const getPage = (pageName = 'π JavaScipt Utils: Part One', url = '/snippets/') => {
return `${pageName} ${url}`
}
console.log(getPage()) // => π JavaScipt Utils: Part One /snippets/
Required Function Parameters
const isRequired = () => {
throw new Error('Missing a mandatory parameter.')
}
const getPage = (pageName = 'π JavaScipt Utils: Part One', url = isRequired()) => {
return `${pageName} ${url}`
}
console.log(getPage()) // => Missing a mandatory parameter.
In the above code, if we do not pass any value for url
, it assumes a default value of the result of isRequired()
method. Since isRequired
throws and error as a return value, that's what we get. This is a simple extension of the Default Function Parameters trick.
Convert timestamp in MS to human-readable format
const formatMS = (ms) => {
if (ms < 0) ms = -ms
const time = {
day: Math.floor(ms / 86400000),
hour: Math.floor(ms / 3600000) % 24,
minute: Math.floor(ms / 60000) % 60,
second: Math.floor(ms / 1000) % 60,
millisecond: Math.floor(ms) % 1000,
}
return (
Object.entries(time)
// keep only non-zero values.
.filter((val) => val[1] !== 0)
// create the string for each value
.map(([key, val]) => `${val} ${key}${val !== 1 ? 's' : ''}`)
// combine the values into a string.
.join(', ')
)
}
formatMS(1001) // => '1 second, 1 millisecond'
formatMS(Date.now()) // => '18785 days, 14 hours, 29 minutes, 36 seconds, 104 milliseconds'
Convert String to Slug
function slugify(string) {
return string
.toString()
.trim()
.toLowerCase()
.replace(/\s+/g, '-')
.replace(/[^\w\-]+/g, '')
.replace(/\-\-+/g, '-')
.replace(/^-+/, '')
.replace(/-+$/, '')
}
console.log(slugify('abra cadabra π£')) // => abra-cadabra
console.log(slugify('abra!@#$% cadabra+_)(*) π£')) // => abra-cadabra
console.log(slugify('abra 123 cadabra π£')) // => abra-123-cadabra
console.log(slugify('abra 123/456/789 cadabra π£')) // => abra-123456789-cadabra
Remove given items from Array (Only primitive type)
function removeItemOnce(arr, value) {
var index = arr.indexOf(value)
if (index > -1) {
arr.splice(index, 1)
}
return arr
}
function removeItemAll(arr, value) {
var i = 0
while (i < arr.length) {
if (arr[i] === value) {
arr.splice(i, 1)
} else {
++i
}
}
return arr
}
console.log(removeItemOnce(['πΊ', 'πΊ', 'π·', 'π₯', 'π·', 'π₯€'], 'πΊ')) // => [ "πΊ", "π·", "π₯", "π·", "π₯€" ]
console.log(removeItemAll(['πΊ', 'πΊ', 'π·', 'π₯', 'π·', 'π₯€'], 'πΊ')) // => [ "π·", "π₯", "π·", "π₯€" ]
Extract text from webpage
const extract = (tag) => {
const result = []
const headings = document.querySelectorAll(tag)
headings.forEach((heading) => {
result.push({
id: heading.id,
text: heading.innerText,
})
})
return result
}
If you run this snippet on this page extract('p')
, you'll get all the text which are wrapped in <p></p>
tag:
[
{
"id": "",
"text": "Part One of the JavaScript Utils series where I share code snippets which can be directly used in your projects. All of the snippets on this page are licensed under CC0-1.0, unless mentioned otherwise."
},
{
"id": "",
"text": "The typical pattern that I see often used is to remove elements that are falsy, which include an empty string \"\", 0, NaN, null, undefined, and false. Using the Boolean constructor function, we can simply return values which are truthy."
},
{
"id": "",
"text": "Bonus: Did you know you can use the copy method in the Developer tools console to quickly copy results of operations? Try it out!"
},
{
"id": "",
"text": "In the above code, if we do not pass any value for url, it assumes a default value of the result of isRequired() method. Since isRequired throws and error as a return value, thatβs what we get. This is a simple extension of the Default Function Parameters trick."
},
{
"id": "",
"text": "If you run this snippet on this page `extract('p')`, you'll get all the text which are wrapped in `<p></p>` tag:"
},
{
"id": "",
"text": "I find this pretty useful to quicky scrap a page without relying on external services or plugins."
},
{
"id": "",
"text": "This is kind of a long one, but a really useful utility. It will add additional properties to the Object using Proxy object. In this case, we are adding the array methods like map, reduce, forEach, filter, slice, find, findKey, includes , keyOf, lastKeyOf."
},
{
"id": "",
"text": "A Proxy object wraps another object and intercepts operations, like reading/writing properties and others, optionally handling them on its own, or transparently allowing the object to handle them."
}
]
I find this pretty useful to quick scrape a page without relying on external services or plugins.
Array Methods on Objects2
This is kind of a long one, but a really useful utility. It will add additional properties to the Object using Proxy
object. In this case, we are adding the array methods like map
, reduce
, forEach
, filter
, slice
, find
, findKey
, includes
, keyOf
, lastKeyOf
.
A Proxy
object wraps another object and intercepts operations, like reading/writing properties and others, optionally handling them on its own, or transparently allowing the object to handle them.
const withArrayMethods = (obj) => {
const methods = {
map(target) {
return (callback) => Object.keys(target).map((key) => callback(target[key], key, target))
},
reduce(target) {
return (callback, accumulator) =>
Object.keys(target).reduce(
(acc, key) => callback(acc, target[key], key, target),
accumulator
)
},
forEach(target) {
return (callback) => Object.keys(target).forEach((key) => callback(target[key], key, target))
},
filter(target) {
return (callback) =>
Object.keys(target).reduce((acc, key) => {
if (callback(target[key], key, target)) acc[key] = target[key]
return acc
}, {})
},
slice(target) {
return (start, end) => Object.values(target).slice(start, end)
},
find(target) {
return (callback) => {
return (Object.entries(target).find(([key, value]) => callback(value, key, target)) ||
[])[0]
}
},
findKey(target) {
return (callback) => Object.keys(target).find((key) => callback(target[key], key, target))
},
includes(target) {
return (val) => Object.values(target).includes(val)
},
keyOf(target) {
return (value) => Object.keys(target).find((key) => target[key] === value) || null
},
lastKeyOf(target) {
return (value) =>
Object.keys(target)
.reverse()
.find((key) => target[key] === value) || null
},
}
const methodKeys = Object.keys(methods)
const handler = {
get(target, prop, receiver) {
if (methodKeys.includes(prop)) return methods[prop](...arguments)
const [keys, values] = [Object.keys(target), Object.values(target)]
if (prop === 'length') return keys.length
if (prop === 'keys') return keys
if (prop === 'values') return values
if (prop === Symbol.iterator)
return function* () {
for (value of values) yield value
return
}
else return Reflect.get(...arguments)
},
}
return new Proxy(obj, handler)
}
Usage
import withArrayMethods from 'utils';
const object = withArrayMethods({
'beer': 'πΊ',
'whiskey': 'π₯',
'wine': 'π·',
'soda': 'π₯€'
});
console.log(object['beer']); // => πΊ
console.log(object.keys); // => Array(4) [ "beer", "whiskey", "wine", "soda" ]
console.log(object.values); // => Array(4) [ "πΊ", "π₯", "π·", "π₯€" ]
console.log(...object); // => πΊ π₯ π· π₯€
console.log(object.slice(0, 2)); // => Array [ "πΊ", "π₯" ]
console.log(object.slice(-1)); // => Array [ "π₯€" ]
console.log(object.find((v, i) => v === 'π₯€')); // => soda
console.log(object.findKey((v, i) => v === 'π₯')); // => whiskey
console.log(object.includes('π₯€')); // => true
console.log(object.includes('coke')); // => false
console.log(object.keyOf('π·')); // => wine
console.log(object.keyOf('wine')); // => null
console.log(object.lastKeyOf('π·')); // => wine
// Array methods
console.log(object.forEach((v, i) => console.log(`${i}: ${v}`));
// => 'beer': 'πΊ', 'whiskey': 'π₯', 'wine': 'π·', 'soda': 'π₯€'
console.log(object.map((v, i) => i + v));
// => Array(4) [ "beerπΊ", "whiskeyπ₯", "wineπ·", "sodaπ₯€" ]
console.log(object.filter((v, i) => v !== 'π₯€'));
// => Object { beer: "πΊ", whiskey: "π₯", wine: "π·" }
console.log(object.reduce((a, v, i) => ({ ...a, [v]: i }), {}));
// => Object { "πΊ": "beer", "π₯": "whiskey", "π·": "wine", "π₯€": "soda" }
And that's it for the Part One of JavaScript Utils. Let me know which one did you found to be most useful.