1

I have an array of objects which I need to filter.

If the object has an active value of true then it should always be returned regardless of other factors.

If searchText is entered then the rest of the list should be filtered by this search text.

If a countrySearch is entered then the list should be filtered by this value, and also by the searchText if that is provided.

const searchText = 'Di';
const countrySearch = "USA";

const myArray = [
    {
        name: "Tom",
        country: "UK",
        active: true
    },
    {
        name: "Dick",
        country: "USA",
        active: false
    },
    {
        name: "Dimmon",
        country: "FR",
        active: false
    }
].filter(item => { 
    if (item.active === true) {
        return true;
    }
  if (searchText) {
    if(item.name.includes(searchText)) {
      return true;
    }
  }
});

console.log(myArray);

In this example the new array should contain Tom as active is true. It should contain Dick as he passes both the searchText and countrySearch test. Dimmon should not be in the new array as he passes only 1 of the searchText and countrySearch search conditions.

It is possible for me to solve this using my current approach but its getting messy with nested if statements. Also my search conditions are likely to grow in complexity overtime. Is this a good case for functional programming?

3
  • 2
    Rather than nest if statements, you could use || and &&. Commented Nov 10, 2017 at 5:37
  • is it OR or AND clause? i mean the result should mett all conditions or any of conditions? Commented Nov 10, 2017 at 5:37
  • 1
    .filter( item => item.active || item.name.includes(searctText) ) Commented Nov 10, 2017 at 5:37

6 Answers 6

3

Here's another way you can do it using transducers

  • an arbitrary number of filters can be used
  • the input array will only be traversed a single time

const comp = ( f , g ) =>
  x => f ( g ( x ) )
  
const compose = ( ...fs ) =>
  fs.reduce ( comp , x => x )
  
const append = ( xs , x ) =>
  xs.concat ( [ x ] )
  
const transduce = ( ...ts ) => xs =>
  xs.reduce ( ts.reduce ( comp ) ( append ) , [] )
  
const filterer = f =>
  k => ( acc , x ) => f ( x ) ? k ( acc , x ) : acc
  
const search = ( query , country, items ) =>
  transduce ( filterer ( x => x.active === true )
            , filterer ( x => x.name.includes ( query ) )
            , filterer ( x => x.country === country )
            ) (items)

const data0 = 
  [ { name: 'Tom'
    , country: 'UK'
    , active: true
    }
  , { name: 'Dick'
    , country: 'USA'
    , active: true
    }
  , { name: 'Dimmon'
    , country: 'FR'
    , active: false
    }
  ]

            
console.log ( search ( 'Di', 'USA', data0 ) )
// [ { name: 'Dick'
//   , country: 'USA'
//   , active: true
//   }
// ]

If you don't want the ceremony that comes with the generalized procedures, you can always build a completely proprietary API

  
const run = ( value = [] ) =>
  ( { type : run
    , value : value
    }
  )

const filterer = f => g =>
  g && g.type === run
    ? g.value.filter ( x => f ( x ) )
    : filterer ( x => f ( x )  && g ( x ) )
    
const search = ( query , country , items ) =>
  filterer ( x => x.active === true )
           ( x => x.name.includes ( query ) )
           ( x => x.country === country )
           ( run ( items ) )

const data0 = 
  [ { name: 'Tom'
    , country: 'UK'
    , active: true
    }
  , { name: 'Dick'
    , country: 'USA'
    , active: true
    }
  , { name: 'Dimmon'
    , country: 'FR'
    , active: false
    }
  ]
            
console.log ( search ( 'Di', 'USA', data0 ) )
// [ { name: 'Dick'
//   , country: 'USA'
//   , active: true
//   }
// ]

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

Comments

2

Given the expected logic you can use active, OR countrySearch AND countrySearch is equal to country AND searchText AND name .includes() searchText, OR searchText AND countrySearch is false AND name .includes() searchText, OR searchText is false AND countrySearch AND countrySearch is equal to country

const searchText = 'Di';
const countrySearch = "USA";

const myArray = [{
      name: "Tom",
      country: "UK",
      active: true
    },
    {
      name: "Dick",
      country: "USA",
      active: false
    },
    {
      name: "Dimmon",
      country: "FR",
      active: false
    }
  ]
  .filter(({active, name, country}) =>
    active 
    || countrySearch && countrySearch === country
      && searchText && name.includes(searchText)
    || searchText && !countrySearch && name.includes(searchText)
    || !searchText && countrySearch && countrySearch === country   
  );
  
console.log(myArray);

Comments

2

Depending on your search method, you can simplify your code as below.

First search joins conditions with OR clause. It simply looks array of objects whose active property is set to true, OR name attribute contains Di lexem - thus all three objects match.

The second one also does the same operations but this time with AND clause, and result is empty array. Because none of objects are helding both conditions at a time.

const searchText = 'Di';
const countrySearch = "USA";

const myArray = [
    {
        name: "Tom",
        country: "UK",
        active: true
    },
    {
        name: "Dick",
        country: "USA",
        active: false
    },
    {
        name: "Dimmon",
        country: "FR",
        active: false
    }
]
console.log('OR')
console.log(myArray.filter(e => e.active || e.name.includes(searchText)));
console.log('AND')
console.log(myArray.filter(e => e.active && e.name.includes(searchText)));

1 Comment

Missing explanation
0

Create a function and pass the searchText & countrySearch. The return from the filter function will be dependent on countrySearch

const searchText = 'Di';
        const countrySearch = "USA";
        const myArray = [
            {
                name: "Tom",
                country: "UK",
                active: true
            },
            {
                name: "Dick",
                country: "USA",
                active: false
            },
            {
                name: "Dimmon",
                country: "FR",
                active: false
            }
        ]
        function getFilterResult(b, c) {
            return myArray.filter(function(a, d) {
           // if countrySearch is undefined then return only active & name
           // which pass the searchText, else return where all the conditions match
                return void 0 === c ? a.active || a.name.includes(b) : 
                a.active || a.name.includes(b) && a.country === c;
            });
        };
    console.log(getFilterResult(searchText,countrySearch));

Comments

0

In this example the new array should contain Tom as active is true. It should contain Dick as he passes both the searchText and countrySearch test. Dimmon should not be in the new array as he passes only 1 of the searchText and countrySearch search conditions.

const searchText = 'Di';
const countrySearch = "USA";

const myArray = [
{
    name: "Tom",
    country: "UK",
    active: true
},{
    name: "Dick",
    country: "USA",
    active: false
},{
    name: "Dimmon",
    country: "FR",
    active: false
}
].filter(item => item.active || (item.name.includes(searchText)
  && item.country.valueOf() === countrySearch.valueOf() ));
document.write(JSON.stringify(myArray));

1 Comment

Is this a good case for functional programming? ~ * ~ Your question has nothing to do with functional programming.
-1

Try:

.filter(item => { 
    if (
            (item.active === true) ||
            (searchText && item.name.includes(searchText))
    ) {
        return true;
    }
  }
})

1 Comment

(item.active === true) || can be also written as ` item.active ||`

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.