npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

sg0

v1.0.59

Published

All the little functions that make functional Node.JS easier.

Downloads

41

Readme

sg0

All the little functions that make functional Node.JS easier.

  • Adds a number of 'fixes' to well-known and used functional-programming staples.
  • Consistent and thorough understanding and tolerance for undefined and null as function inputs.
  • Adds reduceObj(collection, initial, function callback(m,k,v) {...}) to make creation of an object with reduce much easier.
    • You do not have to continually manage m, just return the additions.

undefined and null as Function Inputs

In many situations, it leads to less complex code when functions are tolerant of 'required' inputs being undefined or null. It is sometimes better to return the undefined or null than to truly require it. Consider sg.setOn(obj, keys, value), which allows null-ish anywhere in the keys.

const x   = {a:42};
const b   = sg.deref(x, 'b');               // => b  === undefined
const bb  = sg.deref(x, ['b', b]);          // => bb === undefined

const foo = sg.setOn(x, 'b', 'foo');        // => foo === 'foo';  x  === {a:42,b:'foo'}
const fo2 = sg.setOn(x, ['b', bb], 'foo');  // => fo2 === 'foo';  x  === {a:42,b:'foo'}   /* NO HARM */

reduceObj()

Assumes you are building up an object, so you can just return the new keys/values. You do not need to keep track of the current state of the being-built object.

Consider how AWS uses an Array for the Tags on objects: {..., Tags:[{Name:'a', Value:'b'}]}. Javascript works much better if it were {..., tags:{a:'b'}}

  1. By returning a 2-item Array, you are adding to the object being built.
    • Adding the 'tags' key, and the result of (2).
  2. By returning a 2-item Array, you are adding to the tags object, with Tag.Name as the key, and Tag.Value as the value.
  3. By returning undefined, we are keeping all k/vs that aren't Tags.
    • Could return null which would ignore current k/v, resulting in an object of only {tags:{...}}.
  4. Could also include the original Tags:[{Name:'a', Value:'b'}] by making (2) a many-item Array, and adding the original k/v.
    • Would be better in this case to use ['Tags', v], which is more clear, but [k,v] is the general form.
const awsInstance = await aws.ec2.describeInstances({}).Promise();

const instance = sg.reduceObj(awsInstance, {}, (m,v,k) => {
  if (k === 'Tags') {
    return ['tags', sg.reduceObj(v, {}, (m_tags,Tag) => {    /* 1 */
      return [Tag.Name, Tag.Value];   /* 2 */
    })];
  }
  /* 3 */
});

// instance === {
//   InstanceId: 'abc...',
//   ...
//   tags: {
//     a: 'b'
//   }
// }

// To preserve Tags:
const instance = sg.reduceObj(awsInstance, {}, (m,v,k) => {
  if (k === 'Tags') {
    return [['tags', sg.reduceObj(v, {}, (m_tags,Tag) => {
      return [Tag.Name, Tag.Value];
    })], [k,v]]; /* 4 */
  }
});

Quick Fixes

Fixes a couple of function signatures, putting the callback as the final parameter, where it belongs. These functions simply call into the expected function, rearranging the parameters.

sg.setTimeout(ms, callback);
sg.reduce(collection, initial, callback);

sg.setTimeout(ms, callback)

return setTimeout(callback, ms);

sg.reduce(collection, initial, callback)

Calls _.reduce(collection, callback, initial), not the Javascript-provided Array reduce.

Fix Fixes

I don't know why lodash renamed some of the underscore functions, or removed things, but sg restored these.

sg.min();
sg.max();
sg.rest();
sg.pluck();
sg.head();
sg.last();
sg.initial();
sg.any();
sg.all();
sg.compose();
sg.contains();
sg.findWhere();
sg.indexBy();
sg.invoke();
sg.mapObject();
sg.pairs();
sg.where();

Ease Usage

Similar to the quick fixes, these functions make it easier to call another common function, or create a new function to do something common.

sg.inspect(x, colors [= false]);
sg.firstKey(x);
sg.numKeys(x);
sg.isObject(x);       // x isn't an Array, RegExp, etc.
sg.isPod(x);          // x is a type that doesn't have properties (`.` will not work)
sg.isnt(x);           // x is `null` or `undefined`
sg.trueOrFalse(x);    // sg.tf() is alias

sg.inspect(x, colors [= false])

Calls util.inspect(), but provides the options to do max-depth, and colorize. Returns the decorated message, to pass to console.log() or similar.

sg.firstKey(x) /* nullish ok */

[[x can be nullish; if so, returns x.]]

Returns the first key of the object.

  • Use this function to mean "Does the object have any content?"
  • Many times, an object is supposed to have only one key, for example as a name.
if (!sg.firstKey(obj)) {
  // obj has no content, it is just `{}`
}
const name  = sg.firstKey(obj);
const value = obj[name];

sg.numKeys(x) /* nullish ok */

Returns the number of keys.

sg.isObject(x)

The normal _.isObject() returns true for lots of things that are (technically speaking) Objects, but your code does not want to treat them that way. This function returns true if it is a 'real' object.

Returns true if x is an object, and isn't one of the Object-like things like Array, RegExp, Date, etc. Returns false otherwise.

Notes:

  • Returns false for Error objects.
  • Returns false for nullish objects. (So this function is NOT nullish-input aware.)

sg.isPod(x)

One of the great weaknesses in Javascript is that while (1) if (x) { x.y = 42; /*safe*/} works great to protect against dereferencing x, if x might be null or undefined, (2) if x were the Number 1, x.y = 42 would be unsafe.

This function can be used to make sure x can be dereferenced.

if (!sg.isPod(x) && x) {
  x.y = 42;  /* safe */
}

Notes:

  • Returns false for null and undefined.
    • This makes sense, null and undefined are not PODs. But is counter to the idea that isPod() is used to imply 'is dereferencable'.
  • "POD" means "Plain Old Data".

sg.isnt(x)

x is null or undefined

sg.trueOrFalse(x) [aka sg.tf()]

Returns true or false, following Javascript truthy-ness, but works right for edge cases:

  • The strings 'true' and 'false' are true and false, respectively.
  • Zero (the Number 0) is false.
  • All other Numbers are true.
  • Any string that could be parsed as a Number is treated as such (see above.)
    • The empty string is parsed as 0 (zero).
  • Otherwise:
    • Truthy is true
    • Falsy is false

Synergize and Power Up

sg.kv(obj, key, value);
sg.ap(arr, value);
sg.kkvv(obj, kkey, vvalue, valueName [= 'value']);
sg.dottedKv(obj, key, value);
sg.push(arr, value);
sg.deref(obj, keys);
sg.setOn(obj, keys, value);
sg.setOnn(obj, keys, value);

sg.kv(obj, key, value)

sg.kv(obj, key, value)

Adds value to obj at key. I.e. { ...obj, [key]: value}.

This function was created to make reduce much easier to use and more clear, since augmenting an object with a property is one of the most common uses of reduce.

sg.reduce(data, {}, function(acc, value, key) {
  return sg.kv(acc, key.toLowerCase(), value.toLowerCase());
});

sg.ap(arr, value)

Just like sg.kv, but for pushing a value to the end of an Array.

sg.reduce(data, [], function(acc, value) {
  return sg.ap(acc, value * 2);
});

sg.dottedKv(obj, key, value)

Just like sg.kv, when the key has dots (like would be used with MongoDB's find() to find based on a deep key.)

key can be an array of strings, in which case the strings are .joined('.').

sg.push(arr, value)

Pushes value into arr, and returns the index by which value can be accessed in arr.

var   arr     = ['I said'];
const index   = sg.push(arr, 'booya');

arr[index]   += ', baby!';

console.log(arr.join(' '));     // I said booya, baby!