1

I am building a custom filter pipe for the following structure

[
    {
        "client": {
            "name": "DJASLDJSAKL"
        }, 
        "job_status": {
            "name": "Scheduled"
        }, 
        "descriptor": "Lorem ipsum dolor sit amet consectetur adipiscing elit lacinia quam, ultrices leo interdum senectus integer ultricies venenatis nisl libero, et tellus nec litora volutpat proin duis neque. \r\n\r\nUt metus ac enim mauris malesuada bibendum lectus tincidunt nascetur phasellus, tristique quam libero purus dapibus nisl ultricies urna. \r\n\r\nNisl cubilia donec imperdiet nisi tempus venenatis cras egestas, duis senectus at orci ad porttitor in, magnis suspendisse sem ullamcorper neque tincidunt etiam.\r\n\r\nLitora pulvinar viverra et velit felis massa commodo etiam la.\r\n", 
        "duracion": {
            "text": "00:00:00" 
        }, 
        "tech": {
            "eta_promise_date": "Nov 20 2019 03:05:00:PM"
        }, 
        "siteAddress": "537 West Thomas Drive Rolling Meadows, IL 60008"
    }
]

I first made a function that works only at first level, or top level. Searching I have like an idea, but at the same time I'm kind of lost, this is the method in which I'm working

  findObject(obj, searchTerm){
   Object.keys(obj).forEach((key) => {
     //let r = RegExp(searchTerm, 'gi').test(obj[key]);
     if(obj === searchTerm){
       console.log(`key: ${key}, value: ${obj[key]}`);
       if(typeof obj[key] === 'object'){
         this.findObject(obj[key], searchTerm);
       }
       return null;
     }return null;

   });
  }

But I'm getting an infinite loop at the moment

EDIT Adding the view section

    <ion-list *ngFor="let order of workOrders | filtro: searchTerm">
      <ion-item *ngFor = "let location of order.site" (click)="showOrderLocation(order)">
        <ion-icon name="pin" item-start [ngStyle]="{'color':order.job_status.color}"></ion-icon>
         <h4>{{order.code}}</h4><br>
         <p>{{order.job_status.name}}</p><br>
         <small>ETA: {{order.etaPromise}}</small><br>
         <small>Deadline: {{this.relativeDate(this.utcToLocalTime(order.tech.eta_promise_date))}}</small><br>
         <p>{{order.descriptor}}</p>
      </ion-item>
    </ion-list>
4
  • Is the first parameter of findObject really an object (since it's named obj) or the above specified array and how would a typically search term look like? Commented Dec 9, 2019 at 14:49
  • 1
    recursion issues aside, don't build a recursive filtering pipe. the performance will be abysmal. Commented Dec 9, 2019 at 15:17
  • @uminder sorry, yes, it is the specified array, the searchTerm it's a normal string, like "something" Commented Dec 9, 2019 at 15:20
  • I found this answer link and worked perfectly. Commented Dec 10, 2019 at 18:30

2 Answers 2

2

Your recursion function is a little messy. The findObject function should return a boolean depending on whether or not a property of the object matches the searchTerm.

It's a bad idea to use Object.keys(obj).forEach to iterate over the values because it prevents you from breaking out of the recursion, so use a simple for loop instead. In your case there are two cases in which you want to break out of the recursion: if the string property contains the searchTerm or if a nested object matches the searchTerm.

findObject(obj, searchTerm){
  let pattern = new RegExp(searchTerm, 'gi');
  const values = Object.values(obj)

  for (let value of values) {
    if (typeof value === 'string') {
      // if value i a string, check if it matches!
      if (pattern.test(value)) {
        // match!
        return true
      }
    } else if (typeof value === 'object') {
      // use recursion to check if nested object contains the search term
      const isMatch = this.findObject(value, searchTerm);
      if (isMatch) {
        return true
      }
    }
  }

  // no match, so return false
  return false
}
Sign up to request clarification or add additional context in comments.

2 Comments

Ok, I see, sorry I did not put the view side, I want to show the results and for that I have to return the same structure, I think that maybe should I use Object.entries?, I edit my question adding the view section.
In that case all you need to do is return obj instead of true and null instead of false
2

I woulds split the logic into two separate methods as follows:

const data = [
    {...},
    {...},
    {...}
]

findObject(data: Array, searchTerm: string): Object {
   for (let obj of data) {
     if (this.contains(obj, searchTerm)) {
       return obj;
     }
   }
   return null;
}     

contains(obj: Object, searchTerm: string): boolean {
  for (let value of Object.values(obj)) {
    if (typeof value === 'object') {
      if (contains(value, searchTerm)) {
        return true;
      }
    } else if (value.includes(searchTerm)) {
      return true;
    }
  }
  return false;
}

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.