0

I am attempting to understand how to better use functional programming to achieve cleanliness of code. Ideally, I'd like to use ramda.js for this, but I'm open to any other functional library.

I have two parameters:

  1. emailSearchTerm String
  2. [{ value: 'ACTIVE'}, { value: 'INACTIVE'}] Array of Objects

I have an array I desire to filter by the two parameters above:

[
  {
    email: '[email protected]',
    status: 'ACTIVE'
  },
  {
    email: '[email protected]',
    status: 'INACTIVE'
  },
]

How would one use a pure function that leverages two inputs to effectively filter through an array of objects?

EDIT: Great followup questions:

For now I have used partial filtering using the search term:

searchTerm ? userList.filter(user => user.email.toLowerCase()
    .indexOf(searchTerm.toLowerCase()) > -1) : userList

userList represents the array of objects, while my searchTerm ternary function looks for partial matches. My goal is to extend this function to additionally take an array of status' -- and I'd like to do so in a clean, functional style that is easy to read -- something outside my current skill level. To summarize, the criteria is:

  1. Partial match via email input to email property in userList
  2. Full match in status
  3. both parameters need to be satisfied
3
  • 3
    What is the value of emailSearchTerm? What is the desired result? Which all fields need to be looked up? What is the criteria that governs whether there is a match or not? Commented Oct 31, 2018 at 12:56
  • What have you tried so far? Commented Oct 31, 2018 at 12:57
  • @ilrein I have updated my answer to your needs Commented Oct 31, 2018 at 16:11

3 Answers 3

1

you can use .filter and check if email property contains emailSearchTerm string with .includes() function, and use .some(which will test whether at least one element in the array passes the test implemented by the provided function. ) for checking and filtering via status property

const emailSearchTerm = '[email protected]';
const arrayCheck = [{ value: 'ACTIVE'}, { value: 'INACTIVE'}];

const userList = [
  {
    email: '[email protected]',
    status: 'ACTIVE'
  },
  {
    email: '[email protected]',
    status: 'INACTIVE'
  },
]

const pureFunction = (string, arrayCheck, data) =>
   data.filter(item =>
      item.email.includes(string)
      && arrayCheck.some(obj => obj.value === item.status));

console.log(pureFunction(emailSearchTerm, arrayCheck, userList));

EDIT

my code was updated, I added filter functional into pure function, so you can use it as a pure function with the same data types as now

Sign up to request clarification or add additional context in comments.

Comments

1

Here's a possible approach using vanilla JavaScript.

I take advantage of currying and partial application. Check how what matches email can be any matcher. I provide the partialMatch but you could implement any functions to give more complex matchers.

const equals = x => y => x === y

const contains = xs => x => 
     xs.some (equals (x))
     
const partialMatch = x => y => y.toLowerCase().indexOf (x.toLowerCase()) > -1

// Note that turning that array of objects with  { value: '[STATUS]' }
// should be transformed to an array of string to make things simpler!
const anyStatus = contains (['ACTIVE', 'INACTIVE'])
const activeStatus = contains (['ACTIVE'])

const matchUser = containsStatus => emailMatcher => userList =>
      userList.filter (({ email, status }) =>
          emailMatcher (email)
          && containsStatus (status)
      )

const matchAnyUserStatus = matchUser (anyStatus)
const matchActiveUser = matchUser (activeStatus)

const emailHasAt = partialMatch ('@')
const emailHas123 = partialMatch ('123')

const userList = [{
    email: '[email protected]',
    status: 'ACTIVE'
  },
  {
    email: '[email protected]',
    status: 'INACTIVE'
  }
]

const output1 = matchAnyUserStatus (emailHasAt) (userList)
const output2 = matchAnyUserStatus (emailHas123) (userList)
const output3 = matchActiveUser (emailHas123) (userList)

console.log (output1)
console.log (output2)
console.log (output3)

Comments

1

Here's a solution using Ramda:

const {curry, contains, __, where, filter} = R; // Ramda

const search = curry(function (statuses, emailMatch, list) {
  const email = contains(emailMatch);
  const status = contains(__, statuses);
  return filter(where({email, status}), list);
});

const userList = [
  {email: '[email protected]', status: 'ACTIVE'},
  {email: '[email protected]', status: 'ACTIVE'},
  {email: 'peter@gmail', status: 'INACTIVE'},
  {email: '[email protected]', status: 'INACTIVE'}
];

const searchActiveUsers = search(['ACTIVE']);
const searchAllUsers = search(['ACTIVE', 'INACTIVE']);
const searchActiveSoUsers = searchActiveUsers('stackoverflow');
const searchAllGmailUsers = searchAllUsers('gmail');

console.log(searchActiveSoUsers(userList)); // john
console.log(searchAllGmailUsers(userList)); // david & peter
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.min.js"></script>


Quick explanation:

curry

Takes a function and returns a function that keeps returning a function until all parameters have been supplied. Example:

const sum = curry((a, b, c) => a + b + c);
sum(10)(20)(30); //=> 60
sum(10, 20)(30); //=> 60
sum(10, 20, 30); //=> 60

This allows you to create functions bound with specific parameters:

const searchActiveUsers = search(['ACTIVE']);
const searchActiveSoUsers = searchActiveUsers('stackoverflow');
searchActiveSoUsers(userList); // john

contains

Takes a value and a list and returns true if value is found in the list: (In Ramda, strings and arrays are lists.)

contains('stackoverflow.com', '[email protected]'); //=> true
contains('ACTIVE', ['ACTIVE', 'INACTIVE']); //=> true

__

This is a "placeholder" parameter for curried functions. It allows us to specify parameters later on:

const foundInJohn = contains(__, '[email protected]');
foundInJohn('stackoverflow'); //=> true
foundInJohn('gmail'); //=> false

where

Takes an object of functions and another object and returns true if the second object properties return true when applied to their corresponding functions in the first object:

const soUser = where({email: contains('stackoverflow')});
soUser({email: '[email protected]'}); //=> true
soUser({email: '[email protected]'}); //=> false

filter

This is sort of similar to the native filter method on the Array prototype.

const filterGmail = filter(contains('gmail'));
filterGmail(['[email protected]', '[email protected]']); //=> ['[email protected]']

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.