Why Should Someone Use {} for Initializing an Empty Object in R

Why should someone use {} for initializing an empty object in R?

Your first query

Why should {} be used, this expression only returns NULL, so an NULL assignment would do the same, wouldn't it?

Yes, a <- NULL gives the same effect. Using {} is likely to be a personal style.



NULL

NULL is probably the most versatile and confusing R object. From R language definition of NULL:

It is used whenever there is a need to indicate or specify that an object is absent. It should not be confused with a vector or list of zero length.

The NULL object has no type and no modifiable properties. There is only one NULL object in R, to which all instances refer. To test for NULL use is.null. You cannot set attributes on NULL.

Strictly speaking, NULL is just NULL. And it is the only thing that is.null returns TRUE. However, according to ?NULL:

Objects with value NULL can be changed by replacement operators and will be coerced to the type of the right-hand side.

So, while it is not identical to a length-0 vector with a legitimate mode (not all modes in R are allowed in a vector; read ?mode for the full list of modes and ?vector for what are legitimate for a vector), this flexible coercion often makes it behave like a length-0 vector:

## examples of atomic mode
integer(0) ## vector(mode = "integer", length = 0)
numeric(0) ## vector(mode = "numeric", length = 0)
character(0) ## vector(mode = "character", length = 0)
logical(0) ## vector(mode = "logical", length = 0)

## "list" mode
list() ## vector(mode = "list", length = 0)

## "expression" mode
expression() ## vector(mode = "expression", length = 0)

You can do vector concatenation:

c(NULL, 0L)  ## c(integer(0), 0L)
c(NULL, expression(1+2)) ## c(expression(), expression(1+2))
c(NULL, list(foo = 1)) ## c(list(), list(foo = 1))

You can grow a vector (as you did in your question):

a <- NULL; a[1] <- 1; a[2] <- 2
## a <- numeric(0); a[1] <- 1; a[2] <- 2

a <- NULL; a[1] <- TRUE; a[2] <- FALSE
## a <- logical(0); a[1] <- TRUE; a[2] <- FALSE

a <- NULL; a$foo <- 1; a$bar <- 2
## a <- list(); a$foo <- 1; a$bar <- 2

a <- NULL; a[1] <- expression(1+1); a[2] <- expression(2+2)
## a <- expression(); a[1] <- expression(1+1); a[2] <- expression(2+2)

Using {} to generate NULL is similar to expression(). Though not identical, the run-time coercion when you later do something with it really makes them indistinguishable. For example, when growing a list, any of the following would work:

a <- NULL; a$foo <- 1; a$bar <- 2
a <- numeric(0); a$foo <- 1; a$bar <- 2 ## there is a warning
a <- character(0); a$foo <- 1; a$bar <- 2 ## there is a warning
a <- expression(); a$foo <- 1; a$bar <- 2
a <- list(); a$foo <- 1; a$bar <- 2

For a length-0 vector with an atomic mode, a warning is produced during run-time coercion (because the change from "atomic" to "recursive" is too significant):

#Warning message:
#In a$foo <- 1 : Coercing LHS to a list

We don't get a warning for expression setup, because from ?expression:

As an object of mode ‘"expression"’ is a list ...

Well, it is not a "list" in the usual sense; it is an abstract syntax tree that is list-alike.



Your second query

What is the benefit over a = list(foo = 1, bar = 2)?

There is no advantage in doing so. You should have already read elsewhere that growing objects is a bad practice in R. A random search on Google gives: growing objects and loop memory pre-allocation.

If you know the length of the vector as well as the value of its each element, create it directly, like a = list(foo = 1, bar = 2).

If you know the length of the vector but its elements' values are to be computed (say by a loop), set up a vector and do fill-in, like a <- vector("list", 2); a[[1]] <- 1; a[[2]] <- 2; names(a) <- c("foo", "bar").



In reply to Tjebo

I actually looked up ?mode, but it doesn't list the possible modes. It points towards ?typeof which then points to the possible values listed in the structure TypeTable in src/main/util.c. I have not been able to find this file not even the folder (OSX). Any idea where to find this?

It means the source of an R distribution, which is a ".tar.gz" file on CRAN. An alternative is look up on https://github.com/wch/r-source. Either way, this is the table:

TypeTable[] = {
{ "NULL", NILSXP }, /* real types */
{ "symbol", SYMSXP },
{ "pairlist", LISTSXP },
{ "closure", CLOSXP },
{ "environment", ENVSXP },
{ "promise", PROMSXP },
{ "language", LANGSXP },
{ "special", SPECIALSXP },
{ "builtin", BUILTINSXP },
{ "char", CHARSXP },
{ "logical", LGLSXP },
{ "integer", INTSXP },
{ "double", REALSXP }, /*- "real", for R <= 0.61.x */
{ "complex", CPLXSXP },
{ "character", STRSXP },
{ "...", DOTSXP },
{ "any", ANYSXP },
{ "expression", EXPRSXP },
{ "list", VECSXP },
{ "externalptr", EXTPTRSXP },
{ "bytecode", BCODESXP },
{ "weakref", WEAKREFSXP },
{ "raw", RAWSXP },
{ "S4", S4SXP },
/* aliases : */
{ "numeric", REALSXP },
{ "name", SYMSXP },

{ (char *)NULL, -1 }
};

Are there any downsides to using an empty object initializer?

The compiled result is the same.

The following C#:

static void Main()
{
var x = new List<int>();
var y = new List<int> { };
}

Compiles into the following IL:

.method private hidebysig static 
void Main () cil managed
{
// Method begins at RVA 0x2050
// Code size 14 (0xe)
.maxstack 1
.entrypoint
.locals init (
[0] class [mscorlib]System.Collections.Generic.List`1<int32> x,
[1] class [mscorlib]System.Collections.Generic.List`1<int32> y
)

IL_0000: nop
IL_0001: newobj instance void class [mscorlib]System.Collections.Generic.List`1<int32>::.ctor()
IL_0006: stloc.0
IL_0007: newobj instance void class [mscorlib]System.Collections.Generic.List`1<int32>::.ctor()
IL_000c: stloc.1
IL_000d: ret
} // end of method Program::Main

If you add a value to the collections:

static void Main()
{
var x = new List<int>();
x.Add(1);
var y = new List<int> { 1 };
}

This is the resulting IL:

.method private hidebysig static 
void Main () cil managed
{
// Method begins at RVA 0x2050
// Code size 32 (0x20)
.maxstack 2
.entrypoint
.locals init (
[0] class [mscorlib]System.Collections.Generic.List`1<int32> x,
[1] class [mscorlib]System.Collections.Generic.List`1<int32> y,
[2] class [mscorlib]System.Collections.Generic.List`1<int32> '<>g__initLocal0'
)

IL_0000: nop
IL_0001: newobj instance void class [mscorlib]System.Collections.Generic.List`1<int32>::.ctor()
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: ldc.i4.1
IL_0009: callvirt instance void class [mscorlib]System.Collections.Generic.List`1<int32>::Add(!0)
IL_000e: nop
IL_000f: newobj instance void class [mscorlib]System.Collections.Generic.List`1<int32>::.ctor()
IL_0014: stloc.2
IL_0015: ldloc.2
IL_0016: ldc.i4.1
IL_0017: callvirt instance void class [mscorlib]System.Collections.Generic.List`1<int32>::Add(!0)
IL_001c: nop
IL_001d: ldloc.2
IL_001e: stloc.1
IL_001f: ret
} // end of method Program::Main

This shows how collection initializers are only syntax sugar. Because collection initializers were not originally part of C#, I think people are more used to the constructor syntax. If I came across some code that used an empty collection initializer, I would wonder to myself why, but it certainly doesn't have any severe readability issues. If one is intelligent enough to understand code at all, then {} vs () shouldn't be so upsetting that it undermines one's ability to comprehend what the code is doing. It comes down to a matter of opinion. Do what your team agrees on, and if it's just you, then use it to your hearts content.

TypeScript empty object for a typed variable

Caveats

Here are two worthy caveats from the comments.

Either you want user to be of type User | {} or Partial<User>, or you need to redefine the User type to allow an empty object. Right now, the compiler is correctly telling you that user is not a User. – jcalz

I don't think this should be considered a proper answer because it creates an inconsistent instance of the type, undermining the whole purpose of TypeScript. In this example, the property Username is left undefined, while the type annotation is saying it can't be undefined. – Ian Liu Rodrigues

Answer

One of the design goals of TypeScript is to "strike a balance between correctness and productivity." If it will be productive for you to do this, use Type Assertions to create empty objects for typed variables.

type User = {
Username: string;
Email: string;
}

const user01 = {} as User;
const user02 = <User>{};

user01.Email = "foo@bar.com";

Here is a working example for you.

Here are type assertions working with suggestion.

Why do I see integer rather than Vector for the class of an R vector

In R, "class" is an attribute of an object. However, in R language definition, a vector can not have other attributes than "names" (this is really why a "factor" is not a vector). The function class here is giving you the "mode" of a vector.

From ?vector:

 ‘is.vector’ returns ‘TRUE’ if ‘x’ is a vector of the specified
mode having no attributes _other than names_. It returns ‘FALSE’
otherwise.

From ?class:

 Many R objects have a ‘class’ attribute, a character vector giving
the names of the classes from which the object _inherits_. If the
object does not have a class attribute, it has an implicit class,
‘"matrix"’, ‘"array"’ or the result of ‘mode(x)’ (except that
integer vectors have implicit class ‘"integer"’).

See Here for a bit more on the "mode" of a vector, and get yourself acquainted with another amazing R object: NULL.

To understand the "factor" issue, try your second column:

c2 <- df[, 2]

attributes(c2)
#$levels
#[1] "a" "b" "c"
#
#$class
#[1] "factor"

class(c2)
#[1] "factor"

is.vector(c2)
#[1] FALSE

Create an empty object in JavaScript with {} or new Object()?

Objects

There is no benefit to using new Object(); - whereas {}; can make your code more compact, and more readable.

For defining empty objects they're technically the same. The {} syntax is shorter, neater (less Java-ish), and allows you to instantly populate the object inline - like so:

var myObject = {
title: 'Frog',
url: '/img/picture.jpg',
width: 300,
height: 200
};

Arrays

For arrays, there's similarly almost no benefit to ever using new Array(); over []; - with one minor exception:

var emptyArray = new Array(100);

creates a 100 item long array with all slots containing undefined - which may be nice/useful in certain situations (such as (new Array(9)).join('Na-Na ') + 'Batman!').

My recommendation

  1. Never use new Object(); - it's clunkier than {}; and looks silly.
  2. Always use []; - except when you need to quickly create an "empty" array with a predefined length.

Why can I use an empty value `[]` for a typed array interface but not an empty `{}` for a typed object interface?

Because [] is still a valid array (of length zero) for Array<{name:string;age:number;}>.

But {} is not a valid object of the form {login: string; avatar: string}

Creating an empty object in Python

You can use type to create a new class on the fly and then instantiate it. Like so:

>>> t = type('test', (object,), {})()
>>> t
<__main__.test at 0xb615930c>

The arguments to type are: Class name, a tuple of base classes, and the object's dictionary. Which can contain functions (the object's methods) or attributes.

You can actually shorten the first line to

>>> t = type('test', (), {})()
>>> t.__class__.__bases__
(object,)

Because by default type creates new style classes that inherit from object.

type is used in Python for metaprogramming.

But if you just want to create an instance of object. Then, just create an instance of it. Like lejlot suggests.

Creating an instance of a new class like this has an important difference that may be useful.

>>> a = object()
>>> a.whoops = 1
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'object' object has no attribute 'whoops'

Where as:

>>> b = type('', (), {})()
>>> b.this_works = 'cool'
>>>

Javascript: Having Trouble Understanding Empty Initializer in Reduce Function

The reduce there is doing something slightly more complicated. It's not only assigning to a property of the a, the accumulator, it's also returning the accumulator. This code:

const object_From_Pairs = arr => arr.reduce((a, v) => 
((a[v[0]] = v[1]), a), {});

is equivalent to:

const object_From_Pairs = arr => arr.reduce((a, v) => {
a[v[0]] = v[1];
return a;
}, {});

which is equivalent to

const object_From_Pairs = arr => {
const a = {};
arr.forEach((v) => {
a[v[0]] = v[1];
// there's no need to do anything with a here
// since the a you want to refer to is already in the outer scope
});
};

The empty object is the initial value of the accumulator, which gets a property assigned to it on each iteration. The object does not stay empty.

it seems my new accumulator or a value would be ({}Title = Oliver Twist), {})

That's not valid syntax. On the second iteration, the value of a is

{ Title: 'Oliver Twist' }

so the callback that runs:

((a[v[0]] = v[1]), a)

is like

let a = { Title: 'Oliver Twist' };
return ((a[v[0]] = v[1]), a)

or, without the confusing comma operator:

let a = { Title: 'Oliver Twist' };
a[v[0]] = v[1];
return a;

Even better, I'd highly recommend avoiding .reduce in situations like these - it's just not all that appropriate when the accumulator is the same value every time IMO. Use an ordinary loop instead. arr.forEach or for (const subarr of arr).

For this particular situation, there's an even better option, to allow for the transformation of an array of key-value subarrays into a single object:

const object_From_Pairs = Object.fromEntries;
console.log(object_From_Pairs([['Title', 'Oliver Twist'], ['Author', 'Charles Dickens']]));
// Output: {Title: "Oliver Twist", Author: "Charles Dickens"}


Related Topics



Leave a reply



Submit