7

I've got a following list:

> list(c(3, 4, 5, 8), c(2, 6, 9, 10), c(1, 7))
[[1]]
[1] 3 4 5 8

[[2]]
[1]  2  6  9 10

[[3]]
[1] 1 7

So we can say that 3 belongs to group 1, 6 belongs to group 2, 7 belongs to group 3 and so on. I need a reverse mapping, i.e. to every number I want to have a group id that it belongs to (see expected output):

> list(3, 2, 1, 1, 1, 2, 3, 1, 2, 2)
[[1]]
[1] 3

[[2]]
[1] 2

[[3]]
[1] 1

[[4]]
[1] 1

[[5]]
[1] 1

[[6]]
[1] 2

[[7]]
[1] 3

[[8]]
[1] 1

[[9]]
[1] 2

[[10]]
[1] 2

I thought purrr::transpose should do the job but it doesn't exactly do what I intend, is it? How can it be done?

PS. Ultimately, I need just a vector of a form: 3 2 1 1 1 2 3 1 2 2, but having above I think unlist() is enough to convert.

jakes
  • 1,964
  • 3
  • 18
  • 50
  • My apologies but I can't make out how to get `3 2 1 1 1 2 3 1 2 2` from given input, Its atleast not clear to me. Thanks – PKumar Jan 13 '19 at 07:03
  • @PKumar: 1 is in group 3, 2 is in group 2, 3 is in group 1, 4 is in group 1, 5 is in group 1 ... – Khaynes Jan 13 '19 at 07:08
  • 1
    We start from 1 and see which group 1 belongs to - we can see it's in 3rd group, namely 3rd element of a list. Then we go with 2:10 and check the same. – jakes Jan 13 '19 at 07:08

7 Answers7

3

Here is a base solution ...

list <- list(c(3, 4, 5, 8), c(2, 6, 9, 10), c(1, 7))

rep(1:length(list), sapply(list, length))[order(unlist(list))]
Khaynes
  • 1,976
  • 2
  • 15
  • 27
  • 3
    Nice solution, you could fully vectorize this using `rep(seq_along(l), lengths(l))[order(unlist(l))]` (not sure what you are using `as.numeric` for). – David Arenburg Jan 13 '19 at 08:25
  • 2
    Regarding "doesn't exactly answer my main question with list converting inside-out" I think you need to clarify your question. The way you describe and present your desired result it seems like you simply want to get the list index for each value (whih this and other answers achieve). I fail to see the 'inside-out' part ;) – Henrik Jan 13 '19 at 10:11
  • @DavidArenburg Good point (original solution was a bit longer, so this was just noise left over ;-)) – Khaynes Jan 13 '19 at 22:03
2

Can I suggest an old-fashioned loop:

# your list
x <- list(c(3, 4, 5, 8), c(2, 6, 9, 10), c(1, 7))
# the data in the list as vector
num <- unlist( x )
# the variable that will be the position vector
pos <- NULL

# loop through the possible position, see which number it contains
# find which "group it belongs to, and add that finding to the position vector
for( i in 1:length( num ) )
    for( j in  1:length( x ) )
        if( i %in% x[[j]] ) pos <- c( pos, j )

pos
[1] 3 2 1 1 1 2 3 1 2 2
vaettchen
  • 7,299
  • 22
  • 41
1

Check this solution:

library(tidyverse)
library(magrittr)
library(wrapr)

list(c(3, 4, 5, 8), c(2, 6, 9, 10), c(1, 7)) %.>%
  tibble(x = .) %>%
  mutate(rn = row_number()) %>%
  unnest() %>%
  arrange(x) %$%
  set_names(rn, x) %>%
  as.list()
Paweł Chabros
  • 2,349
  • 1
  • 9
  • 12
1
x <- list(c(3, 4, 5, 8), c(2, 6, 9, 10), c(1, 7))

Following 3 forms will get the same outputs:

library(tidyverse)

# (1)
x %>% set_names(1:3) %>% stack %>% arrange(values) %>% select(ind)

# (2)
x %>% enframe %>% unnest %>% arrange(value) %>% select(name)

# (3)
x %>% (reshape2::melt) %>% arrange(value) %>% select(L1)
Darren Tsai
  • 32,117
  • 5
  • 21
  • 51
1

Also in base, something like this

L <- as.list(setNames( rep(1:length(lengths(l)), lengths(l)), unlist(l)))
# if wanted, sort it with
L[as.character(sort(as.integer(names(L))))]
# if wanted, unname with
unname(L)

with l <- list(c(3, 4, 5, 8), c(2, 6, 9, 10), c(1, 7)).

Or wrapped in a function

list_inside_out <- function (l, unName = TRUE) { 
  l2 <- lengths(l)
  out <- as.list(setNames(rep(1:length(l2), l2), unlist(l)))
  out <- out[as.character(sort(as.integer(names(out))))] 
  if (unName) return(unname(out))
  out
}
list_inside_out(l)
# [[1]]
# [1] 3
# 
# [[2]]
# [1] 2
# 
# [[3]]
# [1] 1
# ...
niko
  • 5,253
  • 1
  • 12
  • 32
0

A solution using purrr. dat2 is the final output, an integer vector.

dat <- list(c(3, 4, 5, 8), c(2, 6, 9, 10), c(1, 7))

library(purrr)

dat2 <- dat %>%
  imap(~set_names(.x, rep(.y, length(.x)))) %>%
  unlist() %>%
  sort() %>%
  names() %>%
  as.integer()
dat2
# [1] 3 2 1 1 1 2 3 1 2 2
www
  • 38,575
  • 12
  • 48
  • 84
0

Using tidyverse and purr::imap_dfr we can create a tibble with the values and indices side by side, arrange by value and pull the indices :

list_ <- list(c(3, 4, 5, 8), c(2, 6, 9, 10), c(1, 7))
library(tidyverse)
imap_dfr(list_,~tibble(.x,.y)) %>% arrange(.x) %>% pull(.y) %>% as.list

# [[1]]
# [1] 3
# 
# [[2]]
# [1] 2
# 
# [[3]]
# [1] 1
# 
# [[4]]
# [1] 1
# 
# [[5]]
# [1] 1
# 
# [[6]]
# [1] 2
# 
# [[7]]
# [1] 3
# 
# [[8]]
# [1] 1
# 
# [[9]]
# [1] 2
# 
# [[10]]
# [1] 2

Less pretty translated in base R (same output) :

with(
  as.data.frame(do.call(rbind,Map(cbind,a = list_, b =seq_along(list_)))),
  as.list(b[order(a)]))
moodymudskipper
  • 46,417
  • 11
  • 121
  • 167