Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lists #282

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open

Lists #282

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions concepts/lists/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"authors": ["colinleach"],
"contributors": [],
"blurb": "A list in R is a collection that can hold heterogeneous data, including other lists."
}
152 changes: 152 additions & 0 deletions concepts/lists/about.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
# About

A `list` in R is an ordered collection of items, differing from `vectors` in a few ways:
- The items can be of different types (e.g. mixing `numeric` with `character`).
- The items can be complex structures (e.g. vectors, other lists, etc).
- There is some different syntax for accessing elements.

Analogies to other languages are imprecise.
An R `list` is somewhat similar to a Python dictionary, but the `list` is ordered and can be accessed by numerical index.
It is somewhat similar to a C `struct`, except that the `list` can easily be extended at runtime.

## Creating lists

Giving names (often called "tags") to the elements is more common with lists than with vectors:

```R
> list(x = 5.3, y = 4.2, color = "blue")
$x
[1] 5.3

$y
[1] 4.2

$color
[1] "blue"
```

However, tags are optional and R will then just use index numbers:

```R
> list(5.3, 4.2, "blue")
[[1]]
[1] 5.3

[[2]]
[1] 4.2

[[3]]
[1] "blue"
```

## Accessing list elements

There are three ways to do this, each useful in different contexts.

### 1. With `$tag` notation

Very common if the tag names are known in advance.

```R
> point <- list(x = 5.3, y = 4.2, color = "blue")
> point$x
[1] 5.3
> point$color
[1] "blue"
```

### 2. With `[[tag]]` notation

This has the advantage that the tag name can be chosen at runtime. Note the double brackets.

```R
> want <- "y"
> point[[want]] # same as point$y
[1] 4.2
```

### 3. With position index

This works whether or not the list has tags. Again, brackets are doubled.

```R
> point[[3]] # same as point$color
[1] "blue"
```

## Adding and deleting elements

Simple: assigning to an element will modify if it exists or add if it is new.

```R
> point$color <- "red"
> point$trust <- TRUE
> str(point) # gives more compact output than the default display
List of 4
$ x : num 5.3
$ y : num 4.2
$ color: chr "red"
$ trust: logi TRUE
```

To delete, assign `NULL` to the unwanted element.

```R
> point$trust <- NULL
> str(point)
List of 3
$ x : num 5.3
$ y : num 4.2
$ color: chr "red"
```

## Lists are "recursive vectors"

The `vector` class is technically known as `atomic vector`, because it can only hold atomic values: `numeric`, `character`, `boolean`, etc.
Because a `list` can contain other lists, it is also known as a `recursive vector`.

As well as being a technical point of nomenclature, this is a reminder that lists can have arbitrarily complex structure.
It is the programmer's responsibility to understand what will be provided when accessing a list element.

## More on list indexing

Some examples above used double brackets `[[ ]]` to access list emements.
This will try to flatten the element to its underlying type, useful if you just want a number or a string.

Using single brackets `[ ]` will not do this flattening, and the returned value will be a sublist.

```R
> point[["color"]]
[1] "red" # flattened

> point["color"]
$color
[1] "red" # sublist
```

Even if the sublist is not what a novice R programmer is likely to want, be aware of it.
For maximum flexibility, many standard R functions return a `list`, even when it contains a single atomic value as here.

To convert to a vector there are two possibilities.
- Take the first element with `[[1]]`
- Use the `unlist()` function.

```R
> pt <-point["color"]

> pt[[1]]
[1] "red" # simple 1-element vector

> unlist(pt)
color
"red" # 1-element vector with name
```

Be careful with `unlist()`. Flattening the structure is useful in this case but can cause surprises with more complicated list elements.


## Advanced Topic: Internal Representation

A `list`, like an atomic `vector`, is stored contiguously. The difference is that numeric and boolean vectors store *values*, but the list stores *references* to objects located elsewhere in memory.

Knowing this is rarely important for an R user, but in a polyglot community like Exercism it may be helpful to programmers familiar with other languages.
111 changes: 111 additions & 0 deletions concepts/lists/introduction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
# Introduction

A `list` in R is an ordered collection of items, differing from `vectors` in a few ways:
- The items can be of different types (e.g. mixing `numeric` with `character`).
- The items can be complex structures (e.g. vectors, other lists, etc).
- There is some different syntax for accessing elements.

Analogies to other languages are imprecise.
An R `list` is somewhat similar to a Python dictionary, but the `list` is ordered and can be accessed by numerical index.
It is somewhat similar to a C `struct`, except that the `list` can easily be extended at runtime.

## Creating lists

Giving names (often called "tags") to the elements is more common with lists than with vectors:

```R
> list(x = 5.3, y = 4.2, color = "blue")
$x
[1] 5.3

$y
[1] 4.2

$color
[1] "blue"
```

However, tags are optional and R will then just use index numbers:

```R
> list(5.3, 4.2, "blue")
[[1]]
[1] 5.3

[[2]]
[1] 4.2

[[3]]
[1] "blue"
```

## Accessing list elements

There are three ways to do this, each useful in different contexts.

### 1. With `$tag` notation

Very common if the tag names are known in advance.

```R
> point <- list(x = 5.3, y = 4.2, color = "blue")
> point$x
[1] 5.3
> point$color
[1] "blue"
```

### 2. With `[[tag]]` notation

This has the advantage that the tag name can be chosen at runtime. Note the double brackets.

```R
> want <- "y"
> point[[want]] # same as point$y
[1] 4.2
```

### 3. With position index

This works whether or not the list has tags. Again, brackets are doubled.

```R
> point[[3]] # same as point$color
[1] "blue"
```

## Adding and deleting elements

Simple: assigning to an element will modify if it exists or add if it is new.

```R
> point$color <- "red"
> point$trust <- TRUE
> point
$x
[1] 5.3

$y
[1] 4.2

$color
[1] "red"

$trust
[1] TRUE
```

To delete, assign `NULL` to the unwanted element.

```R
> point$trust <- NULL
> point
$x
[1] 5.3

$y
[1] 4.2

$color
[1] "red"
```
6 changes: 6 additions & 0 deletions concepts/lists/links.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[
{
"url": "https://intro2r.com/data-structures.html#lists",
"description": "Introduction to R: Lists"
}
]
5 changes: 5 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -586,6 +586,11 @@
"uuid": "2751b6f2-7d71-4397-b063-9bf927a57756",
"slug": "booleans",
"name": "Booleans"
},
{
"uuid": "d2796a75-7571-4b3c-b926-60deae310f8f",
"slug": "lists",
"name": "Lists"
}
],
"key_features": [
Expand Down