Andrew Lerma
Andrew Lerma

Reputation: 31

Finding how many times an instance happens in a list

I have a list and my goal is to determine how many times the values in that list goes above a certain value.

For instance if my list is: List = {0, 0, 3, 3, 4, 0, 4, 4, 4} Id like to know that there were two instances where my values in the list were greater than 2 and stayed above 2. So in this case there were 2 instances, since it dropped below 2 at one point and went above it again.

    private void Report_GeneratorButton_Click(object sender, EventArgs e)
    {
        //Lists
        var current = _CanDataGraph._DataPoints[CanDataGraph.CurveTag.Current].ToList();
        var SOC = _CanDataGraph._DataPoints[CanDataGraph.CurveTag.Soc].ToList();
        var highcell = _CanDataGraph._DataPoints[CanDataGraph.CurveTag.HighestCell].ToList();
        var lowcell = _CanDataGraph._DataPoints[CanDataGraph.CurveTag.LowestCell].ToList();

        //Seperates current list into charging, discharging, and idle
        List<double> charging = current.FindAll(i => i > 2);
        List<double> discharging = current.FindAll(i => i < -2);
        List<double> idle = current.FindAll(i => i < 2 && i > -2);

        //High cell
        List<double> overcharged = highcell.FindAll(i => i > 3.65);
        int ov = overcharged.Count;

        if (ov > 1)
        {
            Console.WriteLine("This Battery has gone over Voltage!");
        }

        else
        {
            Console.WriteLine("This battery has never been over Voltage.");
        }

        //Low cell
        List<double> overdischarged = lowcell.FindAll(i => i > 3.65);
        int lv = overdischarged.Count;

        if (lv > 1)
        {
            Console.WriteLine("This Battery has been overdischarged!");
        }

        else
        {
            Console.WriteLine("This battery has never been overdischarged.");
        }


        //Each value is 1 second
        int chargetime = charging.Count;
        int dischargetime = discharging.Count;
        int idletime = idle.Count;


        Console.WriteLine("Charge time: " + chargetime + "s" + "\n" + "Discharge time: " + dischargetime + "s" + "\n" + "Idle time: " + idletime);

    }

My current code is this and outputs:

    This battery has never been over Voltage.
    This battery has never been overdischarged.
    Charge time: 271s
    Discharge time: 0s
    Idle time: 68

Upvotes: 2

Views: 355

Answers (6)

Rufus L
Rufus L

Reputation: 37050

Another way to do this using System.Linq is to walk through the list, selecting both the item itself and it's index, and return true for each item where the item is greater than value and the previous item is less than or equal to value, and then select the number of true results. Of course there's a special case for index 0 where we don't check the previous item:

public static int GetSpikeCount(List<int> items, int threshold)
{
    return items?
        .Select((item, index) =>
            index == 0
                ? item > threshold
                : item > threshold && items[index - 1] <= threshold)
        .Count(x => x == true)  // '== true' is here for readability, but it's not necessary
           ?? 0;  // return '0' if 'items' is null
}   

Sample usage:

private static void Main()
{
    var myList = new List<int> {0, 0, 3, 3, 4, 0, 4, 4, 4};
    var count = GetSpikeCount(myList, 2);
    // count == 2
}

Upvotes: 0

Eric Lippert
Eric Lippert

Reputation: 660279

There are a great many ways to solve this problem; my suggestion is that you break it down into a number of smaller problems and then write a simple method that solves each problem.

Here's a simpler problem: given a sequence of T, give me back a sequence of T with "doubled" items removed:

public static IEnumerable<T> RemoveDoubles<T>(
  this IEnumerable<T> items) 
{
  T previous = default(T);
  bool first = true;
  foreach(T item in items)
  {
    if (first || !item.Equals(previous)) yield return item;
    previous = item;
    first = false;
  }
}

Great. How is this helpful? Because the solution to your problem is now:

int count = myList.Select(x => x > 2).RemoveDoubles().Count(x => x);

Follow along.

If you have myList as {0, 0, 3, 3, 4, 0, 4, 4, 4} then the result of the Select is {false, false, true, true, true, false, true, true, true}.

The result of the RemoveDoubles is {false, true, false, true}.

The result of the Count is 2, which is the desired result.

Try to use off-the-shelf parts when you can. If you cannot, try to solve a simple, general problem that gets you what you need; now you have a tool you can use for other tasks that require you to remove duplicates in a sequence.

Upvotes: 4

Mustafa Gursel
Mustafa Gursel

Reputation: 832

You can create an extension method as shown below.

public static class ListExtensions
{
    public static int InstanceCount(this List<double> list, Predicate<double> predicate)
    {
        int instanceCount = 0;
        bool instanceOccurring = false;

        foreach (var item in list)
        {
            if (predicate(item))
            {
                if (!instanceOccurring)
                {
                    instanceCount++;
                    instanceOccurring = true;
                }
            }
            else
            {
                instanceOccurring = false;
            }
        }

        return instanceCount;
    }
}

And use your newly created method like this

current.InstanceCount(p => p > 2)

Upvotes: 1

trotunno
trotunno

Reputation: 1

Here's a fairly concise and readable solution. Hopefully this helps. If the limit is variable, just put it in a function and take the list and the limit as parameters.

int [] array = new int [9]{0, 0, 3, 1, 4, 0, 4, 4, 4};
List<int> values = array.ToList();

int overCount = 0;
bool currentlyOver2 = false;

for (int i = 0; i < values.Count; i++) 
{
    if (values[i] > 2)
    {
        if (!currentlyOver2)
            overCount++;

        currentlyOver2 = true;
    }
    else
        currentlyOver2 = false;
}

Upvotes: 0

Cabbage Champion
Cabbage Champion

Reputation: 1213

This solution should achieve the desired result.

    List<int> lsNums = new List<int>() {0, 0, 3, 3, 4, 0, 4, 4, 4} ;
    public void MainFoo(){
        int iChange = GetCritcalChangeNum(lsNums, 2);
        Console.WriteLine("Critical change = %d", iChange);
    }
    public int GetCritcalChangeNum(List<int> lisNum, int iCriticalThreshold) { 
        int iCriticalChange = 0;
        int iPrev = 0;
        lisNum.ForEach( (int ele) => { 
            if(iPrev <= iCriticalThreshold && ele > iCriticalThreshold){
                iCriticalChange++;
            }
            iPrev = ele;
        });

        return iCriticalChange;
    }

Upvotes: 3

Stano Peťko
Stano Peťko

Reputation: 166

public static int CountOverLimit(IEnumerable<double> items, double limit)
{
    int overLimitCount = 0;
    bool isOverLimit = false;

    foreach (double item in items)
    {
        if (item > limit)
        {
            if (!isOverLimit)
            {
                overLimitCount++;
                isOverLimit = true;
            }
        }
        else if (isOverLimit)
        {
            isOverLimit = false;
        }
    }
    return overLimitCount;
}

Upvotes: 0

Related Questions