4

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}

euanjt
  • 152
  • 10
  • 4
    What's wrong with a `for` loop? It would probably be cleaner and easier to understand than a complex Linq query. [See here](http://stackoverflow.com/questions/4881363) for a Linq solution – D Stanley Dec 18 '15 at 20:08
  • Or even `yourBooleanList.ForEach(i => i......);` – Mark C. Dec 18 '15 at 20:10
  • I would use an IEnumerable extension. – Robert McKee Dec 18 '15 at 20:11
  • Also, why would the result from your example not be `{2, 2, 3}`? Or are you just interested in the groups of `true` values? – D Stanley Dec 18 '15 at 20:15
  • Please clarify your question as to whether you just want a count of the largest group true and false bools. – Ben Sewards Dec 18 '15 at 20:16
  • This sounds like a variant of [run-length encoding](https://en.wikipedia.org/wiki/Run-length_encoding) so you might find something useful if you google that. – Steve Cooper Dec 18 '15 at 20:16
  • couldn't you just use a hashset with true/false as the two keys and then do a foreach on the groups of true/false and check if that current count is larger than the corresponding hashset value – Ben Sewards Dec 18 '15 at 20:19

3 Answers3

7

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());
Community
  • 1
  • 1
Robert McKee
  • 21,305
  • 1
  • 43
  • 57
  • 1
    Yes, that pure LINQ code is slightly horrible- I think I'll stick with the extension method – euanjt Dec 18 '15 at 20:54
  • It is, but I added it just because. I'd use the first method which is much faster as well as more readable. – Robert McKee Dec 18 '15 at 20:55
  • People really need to stop suggesting LINQ. Besides the fact that it is horribly slow in every possible implementation, it creates convoluted and incomprehensible vomit-speak-code. It's an absolute nightmare for the guy who sits in your seat when you finally leave. – Krythic Dec 18 '15 at 21:05
  • 1
    @Krythic LINQ has a valuable spot, but like all tools, if you try and use it for everything, you look silly trying to hammer in nails with a screwdriver. On the other hand, saying you should never use it is probably even worse. – Robert McKee Dec 18 '15 at 21:06
  • @Krythic Look on my LINQ answer, there is a way to make it happen and in pretty simple way. Just one Aggregate and two Func's :). Sure, there can be TOO MUCH LINQ, or horrible LINQ, but in large projects, if done "reasonably" it can simplify thinking about the problem that has to be solved. On the other hand, if LINQ ads just next layer of complexity on top, then it screams BAD DESIGN :). – ipavlu Dec 18 '15 at 22:15
1

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}
ipavlu
  • 1,617
  • 14
  • 24
0

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}
ipavlu
  • 1,617
  • 14
  • 24