Skip to content

Commit 30af502

Browse files
committed
Add function consgroupedview
1 parent 4513c5f commit 30af502

File tree

3 files changed

+141
-0
lines changed

3 files changed

+141
-0
lines changed

docs/src/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ This package also defines and exports the following new functions applicable to
99
* [`deepgetindex`](@ref), [`deepsetindex!`](@ref) and [`deepview`](@ref) provide index-based access across multiple layers of nested arrays
1010
* [`innermap`](@ref) and [`deepmap`](@ref) apply a function to the elements of the inner (resp. innermost) arrays.
1111
* [`abstract_nestedarray_type`](@ref) returns the type of nested `AbstractArray`s for a given innermost element type with multiple layers of nesting.
12+
* [`consgroupedview`](@ref) computes a grouping of equal consecutive elements on a vector and applies it to another vector or (named or unnamed) tuple of vectors.
1213

1314

1415
## [ArrayOfSimilarArrays](@id section_ArrayOfSimilarArrays)

src/vector_of_arrays.jl

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,3 +370,115 @@ VectorOfVectors(
370370
similar(elem_ptr, Dims{0}, size(elem_ptr, 1) - 1),
371371
checks
372372
)
373+
374+
375+
376+
"""
377+
consgrouped_ptrs(A::AbstractVector)
378+
379+
Compute an element pointer vector, suitable for creation of a
380+
[`VectorOfVectors`](@ref) that implies grouping equal consecutive entries of
381+
`A`.
382+
383+
Example:
384+
385+
```julia
386+
A = [1, 1, 2, 3, 3, 2, 2, 2]
387+
elem_ptr = consgrouped_ptrs(A)
388+
first.(VectorOfVectors(A, elem_ptr)) == [1, 2, 3, 2]
389+
```
390+
consgrouped_ptrs
391+
Typically, `elem_ptr` will be used to apply the computed grouping to other
392+
data:
393+
394+
```julia
395+
B = [1, 2, 3, 4, 5, 6, 7, 8]
396+
VectorOfVectors(B, elem_ptr) == [[1, 2], [3], [4, 5], [6, 7, 8]]
397+
```
398+
"""
399+
function consgrouped_ptrs end
400+
export consgrouped_ptrs
401+
402+
function consgrouped_ptrs(A::AbstractVector)
403+
elem_ptr = Vector{Int}()
404+
idxs = eachindex(A)
405+
push!(elem_ptr, first(idxs))
406+
if !isempty(A)
407+
prev_0 = A[first(idxs)]
408+
prev::typeof(prev_0) = prev_0
409+
@inbounds for i in (first(idxs) + 1):last(idxs)
410+
curr = A[i]
411+
if (curr != prev)
412+
push!(elem_ptr, i)
413+
prev = curr
414+
end
415+
end
416+
push!(elem_ptr, last(idxs) + 1)
417+
end
418+
elem_ptr
419+
end
420+
421+
422+
"""
423+
consgroupedview(source::AbstractVector, target)
424+
425+
Compute a grouping of equal consecutive elements on `source` via
426+
[`consgrouped_ptrs`](@ref) and apply the grouping to target, resp. each
427+
element of `target`. `target` may be an vector or a named or unnamed tuple of
428+
vectors. The result is a [`VectorOfVectors`](@ref), resp. a tuple of such.
429+
430+
Example:
431+
432+
A = [1, 1, 2, 3, 3, 2, 2, 2]
433+
B = [1, 2, 3, 4, 5, 6, 7, 8]
434+
consgroupedview(A, B) == [[1, 2], [3], [4, 5], [6, 7, 8]]
435+
436+
`consgroupedview` plays well with columnar tables, too:
437+
438+
```julia
439+
using Tables, TypedTables
440+
data = Table(
441+
a = [1, 1, 2, 3, 3, 2, 2, 2],
442+
b = [1, 2, 3, 4, 5, 6, 7, 8],
443+
c = [1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8]
444+
)
445+
446+
result = Table(consgroupedview(data.a, Tables.columns(data)))
447+
```
448+
449+
will return
450+
451+
```
452+
a b c
453+
┌──────────────────────────────────────
454+
1 │ [1, 1] [1, 2] [1.1, 2.2]
455+
2 │ [2] [3] [3.3]
456+
3 │ [3, 3] [4, 5] [4.4, 5.5]
457+
4 │ [2, 2, 2] [6, 7, 8] [6.6, 7.7, 8.8]
458+
```
459+
460+
without copying any data:
461+
462+
```
463+
flatview(result.a) === data.a
464+
flatview(result.b) === data.b
465+
flatview(result.c) === data.c
466+
```
467+
"""
468+
function consgroupedview end
469+
export consgroupedview
470+
471+
function consgroupedview(source::AbstractVector, target::AbstractVector)
472+
elem_ptr = consgrouped_ptrs(source)
473+
VectorOfVectors(target, elem_ptr)
474+
end
475+
476+
function consgroupedview(source::AbstractVector, target::NTuple{N,AbstractVector}) where {N}
477+
elem_ptr = consgrouped_ptrs(source)
478+
map(X -> VectorOfVectors(X, elem_ptr), target)
479+
end
480+
481+
function consgroupedview(source::AbstractVector, target::NamedTuple{syms,<:NTuple{N,AbstractVector}}) where {syms,N}
482+
elem_ptr = consgrouped_ptrs(source)
483+
map(X -> VectorOfVectors(X, elem_ptr), target)
484+
end

test/vector_of_arrays.jl

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,5 +51,33 @@ using UnsafeArrays
5151
@test @uviews A begin
5252
isbits(A[2,2]) == true
5353
end
54+
55+
# -------------------------------------------------------------------
56+
57+
A = [1, 1, 2, 3, 3, 2, 2, 2]
58+
A_grouped_ref = [[1, 1], [2], [3, 3], [2, 2, 2]]
59+
elem_ptr = consgrouped_ptrs(A)
60+
@test first.(@inferred(VectorOfVectors(A, elem_ptr))) == [1, 2, 3, 2]
61+
62+
B = [1, 2, 3, 4, 5, 6, 7, 8]
63+
B_grouped_ref = [[1, 2], [3], [4, 5], [6, 7, 8]]
64+
@test @inferred(VectorOfVectors(B, elem_ptr)) == B_grouped_ref
65+
66+
C = [1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8]
67+
C_grouped_ref = [[1.1, 2.2], [3.3], [4.4, 5.5], [6.6, 7.7, 8.8]]
68+
69+
@test @inferred(consgroupedview(A, B)) isa VectorOfVectors
70+
@test consgroupedview(A, B) == B_grouped_ref
71+
72+
@test @inferred(consgroupedview(A, (B, C))) isa NTuple{2, VectorOfVectors}
73+
@test consgroupedview(A, (B, C)) == (B_grouped_ref, C_grouped_ref)
74+
75+
data = (a = A, b = B, c = C)
76+
@test @inferred(consgroupedview(A, data)) isa NamedTuple{(:a, :b, :c),<:NTuple{3,AbstractVector}}
77+
result = consgroupedview(A, data)
78+
@test result == (a = A_grouped_ref, b = B_grouped_ref, c = C_grouped_ref)
79+
@test flatview(result.a) === data.a
80+
@test flatview(result.b) === data.b
81+
@test flatview(result.c) === data.c
5482
end
5583
end

0 commit comments

Comments
 (0)