M7off
M7off

Reputation: 167

How to find indices for all contiguous elements in an array that appear more than threshold

I'm trying to find indices for all contiguous elements that occurrence more than Threshold in one dimensional integer array using c#

double[] x = new double[20]{1,1,0,0,0,0,1,1,0,0,0,0,1,1,1,0,0,0,1,1};

I want to get indices for this x[] vector as the following for 0 values

threshold=4

start-index[0] =2
  end-index[0] =5

start-index[1] =8
  end-index[1] =11

i try to use this code, but there is many problems in it

public void myFunc(double[] x, ref List<int> start, ref List<int> end,int matchingVal,int threshold)
    {
        int count = 0;
        for (int i = 0; i < x.Length; i++)
        {
            for (int j = i+1; j < threshold; j++)
            {
                if (x[i] == x[j] && x[i] == matchingVal)
                {
                    count++;
                }
                else
                {
                    break;//no contiguous element
                }
                if (count >= threshold)
                {
                    start.Add(i);
                    end.Add(i + count);
                    count = 0;
                }
                else
                    continue;
            }
        }
    }

Upvotes: 0

Views: 302

Answers (2)

TheGeneral
TheGeneral

Reputation: 81493

Updated added more tests and fixed an issue thanks to @AntonínLejsek

Given

public static IEnumerable<(int start, int finish)> GetStuff(int thresh, double[] ary)
{
   int start = 0, count = 1;

   for (var i = 0; i < ary.Length - 1; i++, count++)
      if (ary[i] == ary[i + 1])
      {
         if (count == 1) start = i;
      }
      else
      {
         if (count >= thresh) yield return (start, i);
         count = 0; 
      }

   if (count >= thresh) yield return (start, ary.Length-1);
}

Usage

foreach (var tuple in GetStuff(3,ary))
   Console.WriteLine($"start : {tuple.start}, finish : {tuple.finish}");

Output

var ary = new double[] { 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1 }; 

start : 2, finish : 5
start : 8, finish : 11
start : 12, finish : 14
start : 15, finish : 17

var ary = new double[] { 1, 1, 1 ,0 };

start : 0, finish : 2

var ary = new double[] { 1, 1, 1 };

start : 0, finish : 2

Upvotes: 1

mjwills
mjwills

Reputation: 23898

If you are willing to use MoreLINQ, consider using GroupAdjacent.

An example is below. Basically, it takes the original array, uses Select to include the index, uses GroupAdjacent to group adjacent values together, uses Where and Count to check there are at least 4 adjacent values, then uses Select to project an anonymous type including the value and its first and last index (this can be changed to project to whatever concrete type you want). Then string.Join is used to write it to the console so you can see the results.

using System;
using System.Linq;
using MoreLinq;

namespace Test
{
    class Program
    {
        static void Main(string[] args)
        {
            double[] x = new double[20] { 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1 };

            var results = x.Select((value, index) => new { value, index })
                .GroupAdjacent(z => z.value)
                .Where(z => z.Count() >= 4)
                .Where(z => z.Key == 0) // it is unclear whether you want to filter for specific values - if so, this is how to do it
                .Select(z =>
                    new { value = z.Key, firstIndex = z.First().index, lastIndex = z.Last().index })
                .ToList();

            Console.WriteLine(string.Join(Environment.NewLine, results.Select(z => $"{z.value} - {z.firstIndex} - {z.lastIndex}")));
            Console.ReadLine();
        }
    }
}

Upvotes: 3

Related Questions