What is the best way to combine and elevate functions?

In my database, I have a collection of individuals organized in the following structure:

const people = [
  {name: 'jenny', friends: ['jeff']},
  {name: 'frank', friends: ['jeff', 'ross']},
  {name: 'sarah', friends: []},
  {name: 'jeff', friends: ['jenny', 'frank']},
  {name: 'russ', friends: []},
  {name: 'calvin', friends: []},
  {name: 'ross', friends: ['frank']},
];

I want to filter these individuals based on whether they have friends or not. Additionally, I want to leverage the Predicate of the Array.filter function by lifting it as follows:

const peopleWithoutFriends = people.filter(withoutFriends);
console.log(peopleWithoutFriends);

const peopleWithFriends = people.filter(withFriends);
console.log(peopleWithFriends);

To accomplish this, I can create a generic by function like this:

const by = x => i => {
  return Boolean(get(i, x));
};
const withFriends = by('friends.length');
const peopleWithFriends = people.filter(withFriends);
console.log(peopleWithFriends);

Challenge: Creating a new function for peopleWithoutFriends would require writing an entirely different logic.

const notBy = x => i => {
  return !Boolean(get(i, x));
};

const withOutFriends = notBy('friends.length');
const peopleWithoutFriends = people.filter(withOutFriends);

Instead of duplicating my by function, I prefer to compose smaller functions together.

Inquiry:

How can I effectively implement and utilize small functions such as: flow, Boolean, get, curry, not, and how can I compose withFriends and withoutFriends Predicates for filtering my list of people using Array.filter?

Repl: https://repl.it/@matthewharwood/ChiefWelloffPaintprogram

const {flow, get, curry} = require('lodash');

const people = [
  {name: 'jenny', friends: ['jeff']},
  {name: 'frank', friends: ['jeff', 'ross']},
  {name: 'sarah', friends: []},
  {name: 'jeff', friends: ['jenny', 'frank']},
  {name: 'russ', friends: []},
  {name: 'calvin', friends: []},
  {name: 'ross', friends: ['frank']},
];
const not = i => !i;

const withFriends = i => flow(
  Boolean,
  get(i, 'friends.length'), // Not sure about the arity here, is it possible to lift with curry?
); 

const peopleWithFriends = people.filter(withFriends);
console.log(peopleWithFriends);

const withoutFriends = flow(not, withFriends);
const peopleWithoutFriends = people.filter(withoutFriends);
console.log(peopleWithoutFriends);

Answer №1

When working with functions that return Boolean values, such as the ones used in this example, you can easily get the opposite result by negating the function. Additionally, these functions have an arity of 1, meaning they operate on a single object.

Lodash/fp:

const { flow, get, isEmpty, negate } = _;

const people = [
{name: 'jenny', friends: ['jeff']},
{name: 'frank', friends: ['jeff', 'ross']},
{name: 'sarah', friends: []},
{name: 'jeff', friends: ['jenny', 'frank']},
{name: 'russ', friends: []},
{name: 'calvin', friends: []},
{name: 'ross', friends: ['frank']},
];

const withoutFriends = flow(get('friends'), isEmpty); // create a function that gets the friends array, and check if it is empty
const withFriends = negate(withoutFriends); // negate the result of withoutFriends

const peopleWithFriends = people.filter(withFriends);
console.log(peopleWithFriends);

const peopleWithoutFriends = people.filter(withoutFriends);
console.log(peopleWithoutFriends);
<script src='https://cdn.jsdelivr.net/g/lodash@4(lodash.min.js+lodash.fp.min.js)'></script>

Ramda:

const { pipe, prop, isEmpty, complement } = R;

const people = [
{name: 'jenny', friends: ['jeff']},
{name: 'frank', friends: ['jeff', 'ross']},
{name: 'sarah', friends: []},
{name: 'jeff', friends: ['jenny', 'frank']},
{name: 'russ', friends: []},
{name: 'calvin', friends: []},
{name: 'ross', friends: ['frank']},
];

const withoutFriends = pipe(prop('friends'), isEmpty); // create a function that gets the friends array, and check if it is empty
const withFriends = complement(withoutFriends); // negate the result of withoutFriends

const peopleWithFriends = people.filter(withFriends);
console.log(peopleWithFriends);

const peopleWithoutFriends = people.filter(withoutFriends);
console.log(peopleWithoutFriends);
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>

Notes:

  1. Both _.flow() and R.pipe execute the sequence from left to right (top to bottom). The functions _.compose() and R.compose order is reversed.
  2. The 1st function in flow/pipe/compose gets everything that is passed to the composed function. The other functions in the sequence always get a single param (the result of the previous function)/.
  3. Both Ramda and Lodash have a reject method, which is the opposite of filter, if the predicate returns true, the item is removed. For example, R.reject(foo, xs) is equivalent to R.filter(R.complement(foo), xs). (noted by @ScottSauyet's in this comment)

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

An innovative concept of housing two distinct retailers under one app

I am facing an issue with my vue.js application that consists of a landing page, user dashboard, and admin dashboard. Each section has different states, but they all share a common shared module with important data like page width, token, current user, etc ...

Using jQuery to iterate through elements of a PHP array

I've got a PHP array that outputs like this: Array ( [0] => Array ( [title] => Much title [end] => Such end [start] => Very start ) [1] => Array ( [title] ...

Achieving consistent outcomes across various devices: What's the secret?

Hey there, I am facing an issue with my form page that I created using HTML, CSS, and Javascript. It looks good on my computer but appears messy on a mobile phone. The elements are getting out of the white box (div) making the entire page look chaotic. Sin ...

What are some effective solutions for resolving design problems in Isotope?

When I append the ajax coding, the data is coming fine. However, when I append this style="position: absolute; left: 468px; top: 0px;" css to the new appended data, it is not working. How can I achieve this? Below is my ajax code. Please provide suggestion ...

Save a collection of controller instances within a separate controller in AngularJS

I am in need of an angular example where one controller wraps another. For instance, I am looking to divide some logic between EndpointListController and EndpointController. EndpointListController will handle retrieving data from storage, along with funct ...

Activate Input by Clicking on Label in Internet Explorer version 8

I am trying to implement a widget in JQuery colorbox that allows users to load files. My approach involves using an input tag with type='file' and styling it with visibility:hidden. Additionally, I have created two labels that are styled like but ...

Having difficulties retrieving JSON data

I'm encountering an issue while trying to retrieve my JSON data using an AJAX request. JSON { "Ongoing": [ {"name": "meh"}, {"name": "hem"} ], "Completed": [ {"name": "kfg"}, {"name": "dkfgd"} ] } ...

Universal HTML form validation with a preference for jQuery

Is there a jQuery plugin available for form validation that follows the most common rules? Specifically, I need to validate based on the following criteria: All textboxes must not be empty If the 'Show License' checkbox is checked, then the &a ...

Iterating through elements to set the width of a container

I am currently working on a function that dynamically adjusts the width of an element using JavaScript. Here is my JavaScript code: <script> $('.progress-fill span').each(function(){ var percent = $(this).html(); if(per ...

Angular: Retrieving the Time Format from the Browser

Is there a way to retrieve the time format from the operating system or browser within Angular in order to display time in the user's preferred format? I have attempted to search for a solution, but have come up empty-handed. Thank you in advance! ...

Creating a header upon clicking a link

Is it feasible to establish a request header when a user clicks on a link? ...

Angular retrieves elements between indexes following ordering

After applying the orderBy on a table, the ordering is lost and the values of $index are from the sorted array rather than the original one. For example, if you have an items array with values ['orange', 'banana','potato',&apo ...

How can I rectify the varying vulnerabilities that arise from npm installation?

After running npm audit, I encountered an error related to Uncontrolled Resource Consumption in firebase. Is there a solution available? The issue can be fixed using `npm audit fix --force`. This will install <a href="/cdn-cgi/l/email-protection" clas ...

It is not possible to transfer information to a JSON document using node.js filesystem and browserify

In an attempt to convert incoming input data from a form into JSON format for storage without a backend, I am exploring the use of Node.JS module "fs" or "file-system". To make this work in a browser environment, I am using Browserify for bundling as it r ...

Choose-option "No entries found for that specific query"

Looking for help with setting up a voting function. The vote.js script seems to be working fine, but I'm unable to get the object. Any suggestions? It appears that the POST request is not being sent. Thank you. Encountering this error: Page not foun ...

Passing an object in an ajax call to a function: a comprehensive guide

@model IEnumerable<HitecPoint.BlackBox.Models.SMSReportModal> <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js"> </script> <script type="text/javascript"> var MyAppUrlSettin ...

JavaScript JSON function failing to show

I apologize for my limited knowledge on this matter... We are currently facing an issue with our website's pricing page. The dynamic calculator, which should display the price based on user input of size width and quantity, is not functioning as expe ...

Steps to prevent submission of input field until all necessary fields and checkboxes are filled, disabled the submit button

Check out my basic web application on this sandbox link: codesandbox.io/s/eager-kalam-v1lpg I need assistance with how to prevent the submit button from being enabled until all required fields and checkboxes are filled in. I am fairly new to working with ...

Utilizing a dynamically created Stripe checkout button

Currently, I am attempting to integrate a checkout button from the Stripe Dashboard into my VueJS Project. I have a feeling that I might not be approaching this in the correct manner, so if you have any advice, I would greatly appreciate it. In order to ...

The Puppeteer has obliterated the Execution Context

"Likely attributed to page navigation" The issue I'm encountering with the page in question stems from its navigation process. In order to access the desired content, I must click a specific button. However, upon clicking said button, the c ...