0

I need a function to filter my array and return results based on user inputs.

var Myarray= [
  {Date:"1-Jan-2020",Name:"A", Id: 1},
  {Date:"1-Feb-2020",Name:"B", Id: 2},
  {Date:"1-Mar-2020",Name:"C", Id: 3}
  ...
]

The filter needs to be dynamic. User MUST input at least Date or Name or both together. ID is optional

Is there a smart way to build the filter expression in the function based what ever inputs the user inputs?

More info: I have a UI. The user will have search parameters to choose from for returning date. Date, Name and ID. The user MUST choose at least The Date or Name. The ID is optional but must be considered in the function that will accept the parameters sent. After the users inputs the values, they are passed to a JS function that should filter an array that holds the data. After the filters are applied to the array, a new array is filled with the filtered data. Ex.user sends Date without name or or ID, Then array only filters based on date. Ex2. user sends Name without date or id, then array only filters based on name. Ex3. user sends date and name without id, then array filters based on date and name. Ex4. user sends Date and id without name, then array filters based on date and id. Ex5. user sends name and id without date, then array filters by name and id. ex6 user sends date and name and id, then array filters based on date and name and id. Ex7 user sends id, funtction will NOT allow id to sent alone without either date or name

1
  • Sharing your research helps everyone. Tell us what you've tried and why it didn’t meet your needs. This demonstrates that you’ve taken the time to try to help yourself, it saves us from reiterating obvious answers, and most of all it helps you get a more specific and relevant answer! Also see How to Ask. Commented Apr 10, 2020 at 19:49

3 Answers 3

1

You can use Array.prototype.some (OR) to chain predicates. If at any point a predicate matches, the entire item will be added to the resulting list.

If you want all conditions to match, you will need to use Array.prototype.every (AND) chaining.

I may come back to this, with a more dynamic example, shortly. See below.

const main = () => {
  let arr = [
    { Date: "1-Jan-2020" , Name: "A"  , Id: 1 }, // Yes
    { Date: "1-Feb-2020" , Name: "B"  , Id: 2 }, // Yes
    { Date: "1-Mar-2020" , Name: null , Id: 3 }, // Yes
    { Date: null         , Name: null , Id: 4 }  // No
  ]
  
  console.log(filterWithPredicates(arr, predicates))
}

const predicates = {
  date: (record) => record.Date != null,
  name: (record) => record.Name != null
};

const filterWithPredicates = (list, predicates) => {
  return list.filter(item => {
    return Object.values(predicates).some(predicate => {
      if (predicate(item)) { return true }
    })
    return false
  })
}

main()
.as-console-wrapper { top: 0; max-height: 100% !important; }


Here is an example of predicate chaining.

const Predicates = {
  date: (record) => record.Date != null,
  name: (record) => record.Name != null
}

const main = () => {
  let arr = [
    { Date: "1-Jan-2020" , Name: "A"  , Id: 1 }, // Yes
    { Date: "1-Feb-2020" , Name: "B"  , Id: 2 }, // Yes
    { Date: "1-Mar-2020" , Name: null , Id: 3 }, // Yes
    { Date: null         , Name: null , Id: 4 }  // No
  ]
  
  let filter = new Filter().chain().or(Predicates.date).or(Predicates.name)
  console.log(filter.execute(arr))
}

class Filter {
  constructor() {
    this.filters = []
  }
  chain() {
    this.filters = []
    return this
  }
  or(predicate) {
    this.filters.push({ fn : predicate, op : 'or' })
    return this
  }
  and(predicate) {
    this.filters.push({ fn : predicate, op : 'and' })
    return this
  }
  execute(items) {
    return items.reduce((results, item) => 
      this.__shouldKeepItem(item) ? results.concat(item) : results, [])
  }
  /** @private */
  __startCondition() {
    return this.filters.length ? this.filters[0].op === 'and' ? 1 : 0 : 0
  }
  /** @private */
  __shouldKeepItem(item) {
    return this.filters.reduce((keep, filter) => {
      switch (filter.op) {
        case 'or'  : return keep || filter.fn(item)
        case 'and' : return keep && filter.fn(item)
        default    : return keep
      }
    }, this.__startCondition())
  }
}

main()
.as-console-wrapper { top: 0; max-height: 100% !important; }

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

4 Comments

Your solution seems good however I am quite new to JS and don't understand it much. Also I cant see how you handled the optional Id parameter? I will wait for your more dynamic example
@MutasemSallam If the ID is optional, then you do not filter on it at all.
Can you inline comments in your code on how each line works? I cant understand it. yes the id is optional but the user can still choose it if he wishes to. So it must be consider if it parameter is not null
i added more information to my question. please check it
0

const Myarray= [
  {Date:"1-Jan-2020",Name:"A", Id: 1},
  {Date:"1-Feb-2020",Name:"B", Id: 2},
  {Date:"1-Mar-2020",Name:"C", Id: 3}
];

const customFilter = (array, expr) => {
  if (Object.keys(expr).length == 0) {
    console.log("Error! missing at least one filter expression");
    return null;
  }
  /* if expression doesn't exist it's a pass for all items */
  return array.filter(item => !expr.Date || item.Date == expr.Date).filter(item => !expr.Name || item.Name == expr.Name).filter(item => !expr.Id || item.Id == expr.Id)
}

console.log(customFilter(Myarray, {Date: "1-Jan-2020", Name: "B"}))
console.log(customFilter(Myarray, {Date: "1-Jan-2020"}))
console.log(customFilter(Myarray, {Name: "B"}))
console.log(customFilter(Myarray, {Id: 3}))
console.log(customFilter(Myarray, {}))

3 Comments

I don't need a validation message log. The filter expression should be built dynamically for whatever the user inputs based on the conditions I provided in the summary. Also, The Id is optional however it must be handled in the function if the user wishes to use it.
You need to be more specific when posting a question. Provide a few example input/output pairs. There are many different ways one can interpret what you are asking in terms of coding.
I have a UI. User will have search params to choose for returning data.Date, Name and ID.User MUST choose at least Date or Name.ID is optional but must be considered in function that accepts params sent.After user inputs values, they are sent to JS function that filters array that holds data. After filters are applied to the array, new array is filled with filtered data. Ex.user sends Date without name orID, Then array only filters on date.Ex2.user sends Name without date or id, then array only filters on name.Ex3 user sends id, funtction will NOT allow id to be sent alone without date or name
0

Try:

var filteredArray = Myarray.filter(function (item) {
   return item.Date !== undefined && item.Name !== undefined;
});
var filteredArrayWithOptionalId = Myarray.filter(function (item) {
   return item.Date !== undefined && item.Name !== undefined && item.Id !== undefined;
});

You now have 2 arrays, one with the required parameter objects and one that can handle the optional ID. Then, you can loop through the filteredArrayWithOptionalId to handle the code relating to IDs.

See the answer to a similar question here: How to filter object array based on attributes?

Also I recommend lowercasing your variable names because it is best practice, as long as they are not classes.

2 Comments

i added more information to my question. please check it
Okay, does this solution help better? I made two arrays, one with the optional attribute and one without.

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.