# Vector of Contiguous Booleans to Vector of Unit Ranges

**URL:** <https://discourse.julialang.org/t/vector-of-contiguous-booleans-to-vector-of-unit-ranges/43674>\
**Category:** General Usage\
**Created:** [July 25, 2020, 5:11pm UTC](https://discourse.julialang.org/t/vector-of-contiguous-booleans-to-vector-of-unit-ranges/43674 "2020-07-25T17:11:12Z")\
**Posts on this page:** 15\
**Page:** 1

<div class="post-metadata">

**Author:** ![anon92994695](https://avatars.discourse-cdn.com/v4/letter/a/ce7236/32.png) [@anon92994695](https://discourse.julialang.org/u/anon92994695)\
**Post date:** [July 25, 2020, 5:11pm UTC](https://discourse.julialang.org/t/vector-of-contiguous-booleans-to-vector-of-unit-ranges/43674/1 "2020-07-25T17:11:12Z")

</div>

So consider the following vector: [true true, false, true, true , false] we would want the code to return [1:2,3:3,4:5,6:6]

or: [true true, true, true, true, false] → [1:5, 6:6]

Basically its a groupby or chunk operation, but often times when this shows up you don’t really want/need the overhead of a dataframe.

Anyone else find themselves writing this many times? hoping there’s a better way then the way they’ve written it in the past… I wrote this again in Julia for probably the 10th time and still hate the way I did it.

anyone know of a better less edge case handling on every LOC way?

---

<div class="post-metadata">

**Author:** ![tbeason](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tbeason/32/15898_2.png) [@tbeason](https://discourse.julialang.org/u/tbeason)\
**Post date:** [July 25, 2020, 5:35pm UTC](https://discourse.julialang.org/t/vector-of-contiguous-booleans-to-vector-of-unit-ranges/43674/2 "2020-07-25T17:35:37Z")

</div>

Not a full solution but my first thought would be to use `findall(conditional, x)` to get the locations of the `true`s and then you can iterate on that to stitch them together, filling in where the `false`s live as you go along. Basically would just need to keep track of the start of the sequence, and as long each time you pull the next value you are incrementing by 1 then you are still within a sequence. If not, you know that the sequence was cut and based on how many spots you skipped you know where to put the `false` elements.

---

<div class="post-metadata">

**Author:** ![Zach\_Christensen](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/zach_christensen/32/7220_2.png) [@Zach\_Christensen](https://discourse.julialang.org/u/Zach_Christensen)\
**Post date:** [July 25, 2020, 5:37pm UTC](https://discourse.julialang.org/t/vector-of-contiguous-booleans-to-vector-of-unit-ranges/43674/3 "2020-07-25T17:37:13Z")

</div>

Are you looking for the actual value `[1:5, 6:6]` or the corresponding elements in `a`?

---

<div class="post-metadata">

**Author:** ![DNF](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnf/32/10191_2.png) [@DNF](https://discourse.julialang.org/u/DNF)\
**Post date:** [July 25, 2020, 6:50pm UTC](https://discourse.julialang.org/t/vector-of-contiguous-booleans-to-vector-of-unit-ranges/43674/4 "2020-07-25T18:50:09Z")

</div>

I think you might as well do the last part directly, without first using `findall`.

---

<div class="post-metadata">

**Author:** ![dpsanders](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dpsanders/32/3573_2.png) [@dpsanders](https://discourse.julialang.org/u/dpsanders)\
**Post date:** [July 25, 2020, 7:05pm UTC](https://discourse.julialang.org/t/vector-of-contiguous-booleans-to-vector-of-unit-ranges/43674/5 "2020-07-25T19:05:39Z")

</div>

> [@anon92994695](#):
>
> [true true, false, true, true , false]

How about this:

```julia
julia> v = [true, true, false, true, true , false];

julia> ends = [i for i in 1:length(v)-1 if v[i] != v[i+1]]
3-element Array{Int64,1}:
 2
 3
 5

julia> starts = ends .+ 1

julia> push!(ends, length(v))

julia> pushfirst!(starts, 1)

julia> UnitRange.(starts, ends)
4-element Array{UnitRange{Int64},1}:
 1:2
 3:3
 4:5
 6:6

```

---

<div class="post-metadata">

**Author:** ![yha](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/yha/32/3502_2.png) [@yha](https://discourse.julialang.org/u/yha)\
**Post date:** [July 25, 2020, 7:14pm UTC](https://discourse.julialang.org/t/vector-of-contiguous-booleans-to-vector-of-unit-ranges/43674/6 "2020-07-25T19:14:31Z")

</div>

You are almost asking for run-length encoding, which is implemented in `StatsBase`. Here is a solution based on `StatsBase.rle`:

```julia
using StatsBase, IterTools

function chunks(x)
    _, lens = rle(x)
    [(from+1):to for (from, to) in partition(Iterators.flatten(((0,),cumsum(lens))), 2, 1)]
end

```

---

<div class="post-metadata">

**Author:** ![anon92994695](https://avatars.discourse-cdn.com/v4/letter/a/ce7236/32.png) [@anon92994695](https://discourse.julialang.org/u/anon92994695)\
**Post date:** [July 25, 2020, 7:14pm UTC](https://discourse.julialang.org/t/vector-of-contiguous-booleans-to-vector-of-unit-ranges/43674/7 "2020-07-25T19:14:37Z")

</div>

Not sure what you mean Zach?

I am looking for the vector of unit ranges so the `[1:5, 6:6]` vector in that example, so I can do something with a, or another vector of the same dimenstionality as `a`. I hope that makes sense.

@dpsanders - nice solution I’ve done roughly that way at some point, but you handled some of the edge stuff better than I did (mine was a bit uglier going this route). But I do like this approach probably the most now!

---

<div class="post-metadata">

**Author:** ![anon92994695](https://avatars.discourse-cdn.com/v4/letter/a/ce7236/32.png) [@anon92994695](https://discourse.julialang.org/u/anon92994695)\
**Post date:** [July 25, 2020, 7:19pm UTC](https://discourse.julialang.org/t/vector-of-contiguous-booleans-to-vector-of-unit-ranges/43674/8 "2020-07-25T19:19:57Z")

</div>

@yha - what version of Julia are you running when I run your code with StatsBase loaded I had to add, `Iterators.partition`, and even then I get the following error:

```nohighlight
ERROR: LoadError: MethodError: no method matching partition(::Base.Iterators.Flatten{Tuple{Tuple{Int64},Array{Int64,1}}}, ::Int64, ::Int64)
Closest candidates are:
  partition(::Any, ::Integer) at iterators.jl:1084

```

---

<div class="post-metadata">

**Author:** ![Zach\_Christensen](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/zach_christensen/32/7220_2.png) [@Zach\_Christensen](https://discourse.julialang.org/u/Zach_Christensen)\
**Post date:** [July 25, 2020, 7:25pm UTC](https://discourse.julialang.org/t/vector-of-contiguous-booleans-to-vector-of-unit-ranges/43674/9 "2020-07-25T19:25:44Z")

</div>

Sorry for being a bit cryptic. So if you have the indices `1:6` and you want to do some fancy stuff with that what I do is.

So if I wanted the to have something like `[1:3, 5:6]` I could do one of these:

```julia
julia> using StaticRanges

julia> find_all(or(<(4), >(4)), 1:6). # or find_all(!=(4), 1:6)
5-element GapRange{Int64,UnitRange{Int64},UnitRange{Int64}}:
 1
 2
 3
 5
 6

```

Which returns a vector type (`GapRange`) that strictly contains ranges that are non-continuous a a specific spot to avoid unnecessarily allocating for things like this.

If I wanted to get the elements of `a` corresponding to those indices then I’d do

```julia
julia> using AxisIndices

julia> AxisVector([1,2,3, 5,6,7, 9])[or(<(5), ==(6))]
5-element AxisArray{Int64,1}
 • dim_1 - 1:5
         
  1 1  
  2 2  
  3 3  
  4 5  
  5 7  

```

---

<div class="post-metadata">

**Author:** ![yha](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/yha/32/3502_2.png) [@yha](https://discourse.julialang.org/u/yha)\
**Post date:** [July 25, 2020, 7:27pm UTC](https://discourse.julialang.org/t/vector-of-contiguous-booleans-to-vector-of-unit-ranges/43674/10 "2020-07-25T19:27:00Z")

</div>

I’m using Julia 1.4.2, and `partition` in the code above comes from `IterTools`, not `Iterators`. Looks like the version in `Iterators` doesn’t support the third argument.

---

<div class="post-metadata">

**Author:** ![anon92994695](https://avatars.discourse-cdn.com/v4/letter/a/ce7236/32.png) [@anon92994695](https://discourse.julialang.org/u/anon92994695)\
**Post date:** [July 25, 2020, 8:52pm UTC](https://discourse.julialang.org/t/vector-of-contiguous-booleans-to-vector-of-unit-ranges/43674/11 "2020-07-25T20:52:52Z")

</div>

@yha now it works like a charm. Thanks

@Zach_Christensen - really cool solutions. They don’t explicity answer what I wanted but! I think I see a solution using them. Had no idea those libraries existed, thank you for sharing this.

Okay I think I’ll try to implement a version using your solution, then benchmark some stuff and see what is fastest/easiest to read etc. Then maybe pick a solution(I will not pick my versions of anyones entries you all did the cool stuff I just asked a question).

Thanks everyone

---

<div class="post-metadata">

**Author:** ![piever](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/piever/32/1815_2.png) [@piever](https://discourse.julialang.org/u/piever)\
**Post date:** [July 25, 2020, 9:15pm UTC](https://discourse.julialang.org/t/vector-of-contiguous-booleans-to-vector-of-unit-ranges/43674/12 "2020-07-25T21:15:22Z")

</div>

Not sure if it helps, but StructArrays has something similar (the code will look a bit more complex than necessary because it handles data within a range and with a permutation). You can see the code [here](https://github.com/JuliaArrays/StructArrays.jl/blob/master/src/sort.jl#L8), in particular the `iterate` method.

EDIT: got nerdsniped, this is what that code would look like for your case (without the additional range and permutation):

```julia
julia> struct Chunk{V<:AbstractVector}
           vec::V
       end

julia> Base.IteratorSize(::Type{Chunk{V}}) where {V} = Base.SizeUnknown()

julia> Base.eltype(::Type{Chunk{V}}) where {V} = UnitRange{Int}

julia> function Base.iterate(g::Chunk, i = firstindex(g.vec))
           i1, l = i+1, lastindex(g.vec)
           i > l && return nothing
           @inbounds while i1 <= l && g.vec[i] == g.vec[i1]
               i1 += 1
           end
           return (i:(i1-1), i1)
       end

```

---

<div class="post-metadata">

**Author:** ![Zach\_Christensen](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/zach_christensen/32/7220_2.png) [@Zach\_Christensen](https://discourse.julialang.org/u/Zach_Christensen)\
**Post date:** [July 25, 2020, 10:32pm UTC](https://discourse.julialang.org/t/vector-of-contiguous-booleans-to-vector-of-unit-ranges/43674/13 "2020-07-25T22:32:36Z")

</div>

Yeah, I really need to pull all the “find” related stuff out into its own package so people actually know about it (but for some reason my school insists I keep working on my PhD 😉).

---

<div class="post-metadata">

**Author:** ![anon92994695](https://avatars.discourse-cdn.com/v4/letter/a/ce7236/32.png) [@anon92994695](https://discourse.julialang.org/u/anon92994695)\
**Post date:** [July 25, 2020, 10:34pm UTC](https://discourse.julialang.org/t/vector-of-contiguous-booleans-to-vector-of-unit-ranges/43674/14 "2020-07-25T22:34:41Z")

</div>

@piever very cool approach. My first version of this was an iterator as well. I had huge vectors and just needed to do chunk → emit → process stuff. My current use case is not huge at all, so I was just tinkering.

I’d love to open up benchmarking and stuff but my brain is total soup… The code in my OP is totally wrong and I have no idea what happened. Wrote it was getting the right solutions, then I changed it? Not sure things are a blur right now… Anyways, so be it, I’m gonna call this solved with dpsanders solution because its succinct but too succinct. I

---

<div class="post-metadata">

**Author:** ![anon92994695](https://avatars.discourse-cdn.com/v4/letter/a/ce7236/32.png) [@anon92994695](https://discourse.julialang.org/u/anon92994695)\
**Post date:** [July 25, 2020, 10:36pm UTC](https://discourse.julialang.org/t/vector-of-contiguous-booleans-to-vector-of-unit-ranges/43674/15 "2020-07-25T22:36:33Z")

</div>

same problem right now I have to drum up a project for … life reasons can’t keep making these pit stops. Finish your PhD trust me you’ll be way happier when its all said and done lol. Not that life after a PhD is soooo much better, but it has its perks.
