1

I feel like what I'm looking for should exist, but I don't know what it's called. All searches for "regex for objects" just return tutorials and questions about normal RegEx. Searches for "pattern matching" return news about C# 7's new pattern matching feature, which isn't what I'm trying to accomplish.

To illustrate what I'm after, assume you have the following class:

public class Car
{
    public string Color { get; set; }
    public int MilesDriven { get; set; }
    public bool IsAllWheelDrive { get; set; }
}

And then assume you have a List of Car objects with random and varied properties. I'd like to be able to search through the list for RegEx-like patterns and get the beginning and ending indexes of each instance the pattern occurs.

Example patterns would be:

Find all instances where a white car with all wheel drive occurs between 2 blue cars and the first blue car has more than 1000 miles on it.

Find all instances where a red car is immediately followed by at least 2 green cars and eventually is followed by a car with less than 100 miles on it.

This is a bit of a contrived question, but I would like to know if anything like this exists, preferably as an existing C# library.

Apologies if "pattern-matching" isn't an applicable tag for this question, but as I stated, I don't really know what, if anything else, to call this.

stevcode
  • 171
  • 1
  • 6
  • 1
    I'm not aware of any libraries that do this, but you could perhaps define a `toString()` method on the object and perform Regex on that instead. I'm trying to imagine this as a Linq query (assuming you had some sort of ordering built into the object) and I'd have to imagine that looking really ugly too. – C. Helling May 09 '17 at 18:44
  • @EdPlunkett not even close to a duplicate of that one. This question asks *what* they are actually talking about, there are people who don't even know that LINQ exists, and thus marking this as duplicate because of that would be counter productive. – Krupip May 09 '17 at 18:56
  • @dasblinkenlight I've relied very heavily on LINQ to partially accomplish search for very specific patterns, but it's nothing I'd consider to be the solution I'm looking for (unless I've missed something blindingly obvious). Are you able to write a LINQ expression that would solve either of the example patterns I provided? – stevcode May 09 '17 at 18:56
  • @snb You're right, I didn't read your examples closely enough. – 15ee8f99-57ff-4f92-890c-b56153 May 09 '17 at 19:00
  • @stevcode look at my update, should be more like what you originally wanted – Krupip May 09 '17 at 20:04
  • @stevcode Hi, I'm looking for exactly same library. Did you found anything? Thanks. – TcKs Jun 05 '23 at 21:38
  • 1
    @TcKs Unfortunately, I never found a library, or even a term, for this kind of thing. As C# evolved, particularly the C# 9 pattern matching features, implementing this as my own custom library became easier, but still pretty complicated. The project I needed this for shifted priorities though and I never managed to finish the library. – stevcode Jun 06 '23 at 22:14

1 Answers1

0

What you are looking for is the filter concept. C# does provide an application of such a concept for lists, however it gets complicated if you aren't just trying to filter on the attributes of the object. Microsoft makes the filitering capability generic accross C# with LINQ See this post, or this post for an example:

Filtering collections in C#

Basically the syntax is :

var newlist = list.linqquery1.linquery2...linqueryN.Where(s.x condition);

of course if its simple enough you can do the following:

var newlist = list.Where(s.x condition);

But your problem also calls for selection based on subsequent items in the list. This is a whole lot more complicated because you won't be able to access those elements unless you attach that data to the element in the list. For example, if your listelem was actually a doubly linked list node, you could look ahead in the list for elements after ward based only on a forward node reference and run a condition like this (to check if two green cars follow):

var green2follows = carlist.Where(s.next.type == greencar && s.next.next.type == greencar);

You could, however, concieve of a situation in which it wouldn't be necesary to use doubly linked lists if you implimented this yourself with iteration. Unfortunately, because LINQ works primarily on enumeration based queries, you have to find work around to use Microsofts built in utilities for filtering (though this is not unique to microsoft, typically you don't include locality in queries) This post covers that conclusion.

To do this iteratively, you would create a for loop and test against i + 1, and i + 2 values of cars. Be carful as this gets messy with previous values (i - n) Iterators might be good for this to avoid errors, though i'm not sure if c# supports iterator arithmetic like other languages to allow you to go backwards and forwards. You may be forced to make a custom iterator to generically define this kind of filter.

EDIT: you can avoid creating a custom iterator and merely create a custom object returned by the iterator that supports forward and backward looking via iterator blocks (like the answer suggest in this post)

What you might do is something like this:

HandleObject<T>...
...
public bool backwardsWhere(condition)...
public bool forwardsWhere(condition)...
public bool backwardsNWhere(n, condition);
public bool forwardsNWhere(condition);
//other forwards filter functions for convienience

// get back the element that we wraped
public T get();



static IEnumerable<T> iteratorBlock(List<T> list) 
{ 
    foreach (int i = 0; i < list.Length; i++)
    { 
        // yield means a new HandleObject is only created upon access, and
        // need not be stored otherwise
        yield return HandleObject<T>(i, list); 
    } 
}

Instead of using your list and LINQing over that, use this iterator instead templated on your list, this way you don't have to deal with nasty iterator semantics in C# for creating your own whilist still being able to implement all the functionality you would need to go over the list.

var customIterableFromBlock = iteratorBlock<Car>(carlist);
var selectedCars = from handleCar in customIterableFromBlock
where ... //handleCar.backwardsWhere(...)&& handleCar.forwardsWhere(...) 
          //handleCar.get().x == condition etc
select Car
{
    //get Car from handleCar.get()
};

The reason you would include i and the list itself in the HandleObject constructor is to allow for forward and backward searching through the list based on the position i passed in.

Community
  • 1
  • 1
Krupip
  • 4,404
  • 2
  • 32
  • 54
  • Yeah, the process you've given in your last paragraph is the exact method I've used to far; hard coding a search for one specific pattern per iteration loop. I was hoping something simpler existed before I attempted to make a custom iterator to generically handle any kind of search. – stevcode May 09 '17 at 19:17
  • @stevcode You don't have to have separate patterns per iteration loop, just make the patterns in the same loop. `for(...) { if condition1(index, list) && condition2(index, list)... etc}` you can do the conditions in the same loop – Krupip May 09 '17 at 19:24
  • @stevcode Addtionally despite what you describe actually being a pretty common pattern, due to the nature of imperative languages doing this outside of iteration becomes an issue. In Haskell /other functional languages you wouldn't have to deal with this issue. You can group elements together, filter based on those groups and get the correct value out. In an imperative language you could do this, but you would be forced to parse the entire list before hand if you wanted to do a filter. Because Haskell is lazily evaluated you don't take performance hits doing this unlike C#,C,C++,Java etc.. – Krupip May 09 '17 at 19:30