euanjt
euanjt

Reputation: 152

How to collapse list of bools into list of ints

If I have a list of booleans, how do I convert into into a list (or any other IEnnumerable<int>) of integers where each integer represents the length of each string of booleans using LINQ?

For example {true, true, false, false, true, true, true} would become {2, 3} as the first run of trues is 2 long, and the second is 3 long. Or must I go back to for and foreach loops?

I am not interested in the number of false values, they are only important because they separate runs of true values.

So {true, true, true, false, true, true, true, true, false, false, true, true, true} would become {3, 4, 3}

Upvotes: 4

Views: 492

Answers (3)

ipavlu
ipavlu

Reputation: 1659

There is no bad LINQ, just bad ideas:). It can be done with LINQ pretty nicely and still, one knows what it does.

Well, I hope so:).

List<bool> lst = new List<bool>() { true, true, true, false, true, true,
                                    true, true, false, false, true, true,
                                    true };

var bb =
lst
.Aggregate(new List<int>(), (acc, element) =>
{
    if (element == true && acc.Count < 1) acc.Add(1);
    else if (element == true && acc.Count > 0) acc[acc.Count - 1] = acc[acc.Count - 1]++;
    else if(acc.Count > 0 && acc[acc.Count - 1] > 0) acc.Add(0);
    return acc;
}, acc =>
{
    if (acc.Count > 0 && acc[acc.Count - 1] == 0)
    {
        acc.RemoveAt(acc.Count - 1);
    }
    return acc;
});
//{3, 4, 3}

Upvotes: 1

ipavlu
ipavlu

Reputation: 1659

some time ago I made a generic version that looks for consecutive elements. It works on anything implementing IComparable and it is possible to choose, what is the interested element.

Just a food for thought:).

public
static
class IEnumerableExtensions
{
    public
    static
    IEnumerable<int> CountConsecutiveElements<TElement>(this IEnumerable<TElement> ie,
                                                             TElement consecutive_element)
        where TElement: IComparable<TElement>
    {
        using(var en = ie.GetEnumerator())
        {
            int i = 0;
            while (en.MoveNext())
            {
                if (en.Current.CompareTo(consecutive_element) == 0) i++;
                else if (i > 0)
                {
                    yield return i;
                    i = 0;
                }
            }
            if (i > 0) yield return i;
        }
    }
}

And the use is then simple:

List<bool> lst = new List<bool>() { true, true, true, false, true, true,
                                    true, true, false, false, true, true,
                                    true };

//var rslt = lst.CountConsecutiveElements(true).ToList();
//there is no need for .ToList()

var rslt = lst.CountConsecutiveElements(true);
//{3, 4, 3}

Upvotes: 0

Robert McKee
Robert McKee

Reputation: 21477

Something like this:

public static class IEnumerableExt
{
    public static IEnumerable<int> ConsecutiveTrues(this IEnumerable<bool> bools)
    {
      var flag=false;
      int count=0;
      foreach(var b in bools)
      {
        if (b)
        {
          count++;
        } else if (flag)
        {
            yield return count;
            count=0;
        }
        flag=b;
      }
      if (flag)
        yield return count;
    }
}

then used like:

void Main()
{
  var bools=new bool[]{true, true, false, false, true, true, true};
  var results=bools.ConsecutiveTrues();
}

Using a pure LINQ way (taken mostly from https://stackoverflow.com/a/27574266/856353):

var str = new bool[]{true, true, true, false, true, true, true, true, false, false, true, true, true};

// Retain a horrid mutable sequence which tracks consecutive characters
var sequence = 0;
var grps = str
  .Zip((str.Concat(new bool[]{str.Last()})).Skip(1),
     (prev, current) => new { prev, current })
  .GroupBy(p => new { 
      Ch = p.prev, 
      Sequence = p.current == p.prev
      ? sequence 
      : sequence++})
  .Where(l=>l.Key.Ch==true)
  .Select(l=>l.Count());

Upvotes: 7

Related Questions