Jules
Jules

Reputation: 7233

Find consecutive elements in list that match expression

I would like to retrieve the elements from a list that (in consecutive order) match an expression, but for the moment I don't see the (performant) solution.

E.g.:

var items = new List<string>
{
   "1",
   ".",
   "249",
   "something",
   "1", // 5
   ".", // 6
   "250", // 7
   "yes"
};

// I am looking for the consecutive elements that form the expression below (5-6-7).
var match = "1.250";
var elements = ...;

Upvotes: 0

Views: 586

Answers (4)

elmer007
elmer007

Reputation: 1445

Here's a way that's straightforward and isn't nested more than 1 level.

This will find the first match. If no match is found, indexes will have a count of 0 after the loop. Put inside a method of your choosing.

List<int> indexes = new List<int>();
for (int i = 0; i < items.Count; i++)
{
    string temp = items[i];
    while (temp.Length < match.Length && temp == match.Substring(0, temp.Length) && i < items.Count - 1)
    {
        indexes.Add(i + 1); // example was given using 1-based
        temp += items[++i];
    }

    if (temp == match)
    {
        indexes.Add(i + 1);
        break; // at this point, indexes contains the values sought
    }

    indexes.Clear();
}

With a list of 10,000 elements, where the elements to find are at the end, this runs in about 0.0003775 seconds.

Upvotes: 2

Matt.G
Matt.G

Reputation: 3609

  public IEnumerable<int> GetMatch(List<string> items, string match)
  {
     string str = null;
     for (int i = 0; i < items.Count; i++)
     {
        if (!match.StartsWith(items[i]))
           continue;
        for (int j = 1; j < items.Count - i + 1; j++)
        {
           str = items.Skip(i).Take(j).Aggregate((x, y) => x + y);
           if (str.Equals(match))
              return Enumerable.Range(i + 1, j).Select(x => x).ToList();
           else if (match.StartsWith(str))
              continue;
           break;
        }
     }
     return new int[0];
  }



  [Fact]
  public void Test()
  {
     var items = new List<string> { "1", ".", "249", "something", "1", ".", "250", "yes" };
     var match = "1.250";
     var matchingIndexes = GetMatch(items, match);
     Assert.True(matchingIndexes.Any());
     Assert.Equal(5, matchingIndexes.ElementAt(0));
     Assert.Equal(6, matchingIndexes.ElementAt(1));
     Assert.Equal(7, matchingIndexes.ElementAt(2));
  }

Upvotes: 0

jdweng
jdweng

Reputation: 34433

Try IEquatable :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;


namespace ConsoleApplication137
{
    class Program
    {
        static void Main(string[] args)
        {
            List<string> ipAddresses = new List<string>() {
                "172.15.15.1",
                "172.15.15.2",
                "172.15.15.3",
                "172.15.15.4",
                "172.16.15.1",
                "172.17.15.1"
            };

            MatchIp matchIP = new MatchIp("172.15", "172.16");
            List<string> results = ipAddresses.Where(x => matchIP.Equals(x)).ToList();
        }
    }
    public class MatchIp : IEquatable<string>
    {
        int[] startAddress { get; set; }
        int[] endAddress { get; set; }
        int length { get; set; }

        public MatchIp(string startAddressStr, string endAddressStr)
        {
            startAddress = startAddressStr.Split(new char[] { '.' }).Select(x => int.Parse(x)).ToArray();
            endAddress = endAddressStr.Split(new char[] { '.' }).Select(x => int.Parse(x)).ToArray();
            length = Math.Min(startAddress.Count(), endAddress.Count());
        }

        public Boolean Equals(string ip)
        {
            Boolean results = true;

            try
            {
                int[] address = ip.Split(new char[] { '.' }).Select(x => int.Parse(x)).ToArray();
                if (address.Length == 4)
                {
                    for (int i = 0; i < length; i++)
                    {
                        if ((address[i] < startAddress[i]) || (address[i] > endAddress[i]))
                        {
                            results = false;
                            break;
                        }
                    }
                }
                else
                {
                    results = false;
                }
            }
            catch (Exception ex)
            {
                results = false;
            }

            return results;
        }
    }

}

Upvotes: 0

Guenhwyvar
Guenhwyvar

Reputation: 41

This should give an answer but if there is more than one matches it returns the last one.

 public static void GetConsecutiveMatch(List<String> items, String match, out List<int> indices)
    {
        indices = new List<int>();
        for (int i = 0; i < items.Count(); i++)
        {
            var strToCompare = "";
            for (int j = i ;  j < items.Count(); j++)
            {
                strToCompare += items[j];
                if (strToCompare.Length == match.Length)
                {
                    if (strToCompare == match)
                    {
                        indices.Clear();
                        for (int k = i; k <= j; k++)
                        {
                            indices.Add(k + 1); // at your example indices seems to be starting at 1, so I added 1 to the actual index
                        }
                    }

                    break;              
                }
                else if (strToCompare.Length > match.Length)
                {
                    break;
                }
            }
        }
    }

Upvotes: 1

Related Questions