?s 00:46 de 22/02/2025, Dennis Fisher escreveu:> R 4.4.0 > OS X > > Colleagues > > I have a sequence like: > 1, 3, 4, 5, 7, 8, 12, 13, 14, 15, 20 > > I would like to display it as: > 1, 3-5, 7-8, 12-15, 20 > > Any simple ways to accomplish this? > > Dennis > > > Dennis Fisher MD > P < (The "P Less Than" Company) > Phone / Fax: 1-866-PLessThan (1-866-753-7784) > www.PLessThan.com > > > [[alternative HTML version deleted]] > > ______________________________________________ > R-help at r-project.org mailing list -- To UNSUBSCRIBE and more, see > https://stat.ethz.ch/mailman/listinfo/r-help > PLEASE do read the posting guide https://www.R-project.org/posting-guide.html > and provide commented, minimal, self-contained, reproducible code.Hello, Here is a way with package R.utils, function seqToIntervals. x <- scan(text = "1, 3, 4, 5, 7, 8, 12, 13, 14, 15, 20", sep = ",") mat <- R.utils::seqToIntervals(x) apply(mat, 1L, \(m) { ifelse(m[1L] == m[2L], m[1L], paste(m, collapse = "-")) }) #> [1] "1" "3-5" "7-8" "12-15" "20" If you want to be fancy, define a special class that prints like that. x <- scan(text = "1, 3, 4, 5, 7, 8, 12, 13, 14, 15, 20", sep = ",") as_seqInterval <- function(x) { old_class <- class(x) class(x) <- c("seqInterval", old_class) x } print.seqInterval <- function(x, ...) { mat <- R.utils::seqToIntervals(x) out <- apply(mat, 1L, \(m) { ifelse(m[1L] == m[2L], m[1L], paste(m, collapse = "-")) }) print(out) } y <- as_seqInterval(x) class(y) #> [1] "seqInterval" "numeric" # autoprinting y y #> [1] "1" "3-5" "7-8" "12-15" "20" # explicit printing y print(y) #> [1] "1" "3-5" "7-8" "12-15" "20" Hope this helps, Rui Barradas -- Este e-mail foi analisado pelo software antiv?rus AVG para verificar a presen?a de v?rus. www.avg.com
Well, as I predicted, my initial suggestions were, ... ummm, rather dumb.
Also, Rui's suggestions are probably preferable to the below. However, it
*is* a very simple, bare-boned approach to converting a sequence of
increasing integers to a character representation using interval notation.
The code is *very* simple-minded and needs no explanation, I think. You
should also be able to easily tweak it to change the output format from my
paste0(..., collapse) choice to something else.
compr <- function(x, sep ="-", collapse = ", ")
{
left<- c(TRUE, diff(x) != 1)
right <- c(rev(diff(rev(x)) != -1 ), TRUE)
xleft <- x[left]
xright <- x[right]
ifelse(xleft == xright,
xleft,
paste(xleft, xright, sep = sep)) |>
paste0(collapse = collapse)
}
which yields the following with your example
> compr(c( 1, 3, 4, 5, 7, 8, 12, 13, 14, 15, 20))
[1] "1, 3-5, 7-8, 12-15, 20" ##Note: A single character string
(Do let me know if this can fail)
Cheers,
Bert
"An educated person is one who can entertain new ideas, entertain others,
and entertain herself."
On Sat, Feb 22, 2025 at 4:36?AM Rui Barradas <ruipbarradas at sapo.pt>
wrote:
> ?s 00:46 de 22/02/2025, Dennis Fisher escreveu:
> > R 4.4.0
> > OS X
> >
> > Colleagues
> >
> > I have a sequence like:
> > 1, 3, 4, 5, 7, 8, 12, 13, 14, 15, 20
> >
> > I would like to display it as:
> > 1, 3-5, 7-8, 12-15, 20
> >
> > Any simple ways to accomplish this?
> >
> > Dennis
> >
> >
> > Dennis Fisher MD
> > P < (The "P Less Than" Company)
> > Phone / Fax: 1-866-PLessThan (1-866-753-7784)
> > www.PLessThan.com
> >
> >
> > [[alternative HTML version deleted]]
> >
> > ______________________________________________
> > R-help at r-project.org mailing list -- To UNSUBSCRIBE and more, see
> > https://stat.ethz.ch/mailman/listinfo/r-help
> > PLEASE do read the posting guide
> https://www.R-project.org/posting-guide.html
> > and provide commented, minimal, self-contained, reproducible code.
> Hello,
>
> Here is a way with package R.utils, function seqToIntervals.
>
>
> x <- scan(text = "1, 3, 4, 5, 7, 8, 12, 13, 14, 15, 20", sep =
",")
>
> mat <- R.utils::seqToIntervals(x)
> apply(mat, 1L, \(m) {
> ifelse(m[1L] == m[2L], m[1L], paste(m, collapse = "-"))
> })
> #> [1] "1" "3-5" "7-8"
"12-15" "20"
>
>
> If you want to be fancy, define a special class that prints like that.
>
>
>
> x <- scan(text = "1, 3, 4, 5, 7, 8, 12, 13, 14, 15, 20", sep =
",")
>
> as_seqInterval <- function(x) {
> old_class <- class(x)
> class(x) <- c("seqInterval", old_class)
> x
> }
> print.seqInterval <- function(x, ...) {
> mat <- R.utils::seqToIntervals(x)
> out <- apply(mat, 1L, \(m) {
> ifelse(m[1L] == m[2L], m[1L], paste(m, collapse = "-"))
> })
> print(out)
> }
>
> y <- as_seqInterval(x)
> class(y)
> #> [1] "seqInterval" "numeric"
>
> # autoprinting y
> y
> #> [1] "1" "3-5" "7-8"
"12-15" "20"
>
> # explicit printing y
> print(y)
> #> [1] "1" "3-5" "7-8"
"12-15" "20"
>
>
> Hope this helps,
>
> Rui Barradas
>
>
> --
> Este e-mail foi analisado pelo software antiv?rus AVG para verificar a
> presen?a de v?rus.
> www.avg.com
>
> ______________________________________________
> R-help at r-project.org mailing list -- To UNSUBSCRIBE and more, see
> https://stat.ethz.ch/mailman/listinfo/r-help
> PLEASE do read the posting guide
> https://www.R-project.org/posting-guide.html
> and provide commented, minimal, self-contained, reproducible code.
>
[[alternative HTML version deleted]]
Rui,
Your post (way at the bottom) expands beyond what was requested and that makes
it more interesting.
I like to look at what I consider reusable ideas and tools.
One was the reminder of using apply across rows of a matrix which would have
applied to my earlier post of a package that also created a matrix of sequential
followed by a count.
Another was the idea of creating a changed object that now has an additional
class that can determine which new print function to use automagically. I do
wonder what happens if there are other classes that also have such a function.
Does the first class in the listing dominate?
Finally, I have been considering the broader question of how to create a more
abstract function along the lines being discussed.
This request was about integers and the definition of being next but contiguous
was to be one apart. Clearly, there can be cases where you want say just
contiguous even (or odd) numbers so that c(2, 6,8,10) becomes "2,
6-10" as in street addresses for deliveries on one side of a street where
all addresses on that side are even. That could be handled by not hard-coding in
a difference of 1, but providing a successor() function that returns TRUE/FALSE
when supplied with two arguments. One method is to allow such a function as an
argument.
A related idea would be to allow other entries where a successor has meaning.
Consider c("a", "c", "d", "e",
"x", "y") which you can imagine collapsed as "a, c-e,
x-y" as one example. But you can broaden it to work with something like
Roman numerals in text form. Having a successor function, as above, could allow
such additional functionality.
And, of course, other options could be allowed, perhaps to just pass through, so
that you can specify the sep(arator) or col(lapse) strings such as instead of
this:
"1, 3-6, 12-19"
You might see:
1
3:6
12:19
As in multiple lines and colon separated.
But one question I keep asking is about complexity. We have seen a number of
solutions and some of them require many passes over what could be a long vector
and other data structures like matrices with quite a few passes. It is nice to
re-use bits and pieces but perhaps for longer examples, not in prototyping mode,
writing a custom function that passes minimally over the data, may be a better
choice.
-----Original Message-----
From: R-help <r-help-bounces at r-project.org> On Behalf Of Rui Barradas
Sent: Saturday, February 22, 2025 7:36 AM
To: Dennis Fisher <fisher at plessthan.com>; r-help <r-help at
r-project.org>
Subject: Re: [R] Compressing a sequence
?s 00:46 de 22/02/2025, Dennis Fisher escreveu:> R 4.4.0
> OS X
>
> Colleagues
>
> I have a sequence like:
> 1, 3, 4, 5, 7, 8, 12, 13, 14, 15, 20
>
> I would like to display it as:
> 1, 3-5, 7-8, 12-15, 20
>
> Any simple ways to accomplish this?
>
> Dennis
>
>
> Dennis Fisher MD
> P < (The "P Less Than" Company)
> Phone / Fax: 1-866-PLessThan (1-866-753-7784)
> www.PLessThan.com
>
>
> [[alternative HTML version deleted]]
>
> ______________________________________________
> R-help at r-project.org mailing list -- To UNSUBSCRIBE and more, see
> https://stat.ethz.ch/mailman/listinfo/r-help
> PLEASE do read the posting guide
https://www.R-project.org/posting-guide.html
> and provide commented, minimal, self-contained, reproducible code.
Hello,
Here is a way with package R.utils, function seqToIntervals.
x <- scan(text = "1, 3, 4, 5, 7, 8, 12, 13, 14, 15, 20", sep =
",")
mat <- R.utils::seqToIntervals(x)
apply(mat, 1L, \(m) {
ifelse(m[1L] == m[2L], m[1L], paste(m, collapse = "-"))
})
#> [1] "1" "3-5" "7-8"
"12-15" "20"
If you want to be fancy, define a special class that prints like that.
x <- scan(text = "1, 3, 4, 5, 7, 8, 12, 13, 14, 15, 20", sep =
",")
as_seqInterval <- function(x) {
old_class <- class(x)
class(x) <- c("seqInterval", old_class)
x
}
print.seqInterval <- function(x, ...) {
mat <- R.utils::seqToIntervals(x)
out <- apply(mat, 1L, \(m) {
ifelse(m[1L] == m[2L], m[1L], paste(m, collapse = "-"))
})
print(out)
}
y <- as_seqInterval(x)
class(y)
#> [1] "seqInterval" "numeric"
# autoprinting y
y
#> [1] "1" "3-5" "7-8"
"12-15" "20"
# explicit printing y
print(y)
#> [1] "1" "3-5" "7-8"
"12-15" "20"
Hope this helps,
Rui Barradas
--
Este e-mail foi analisado pelo software antiv?rus AVG para verificar a presen?a
de v?rus.
www.avg.com
______________________________________________
R-help at r-project.org mailing list -- To UNSUBSCRIBE and more, see
https://stat.ethz.ch/mailman/listinfo/r-help
PLEASE do read the posting guide https://www.R-project.org/posting-guide.html
and provide commented, minimal, self-contained, reproducible code.