# Object.isEmpty

**URL:** <https://es.discourse.group/t/object-isempty/166>\
**Category:** 💡 Ideas\
**Tags:** proposal\
**Created:** [December 18, 2019, 4:55pm UTC](https://es.discourse.group/t/object-isempty/166 "2019-12-18T16:55:55Z")\
**Posts on this page:** 14\
**Page:** 1

<div class="post-metadata">

**Author:** ![IagoLast](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/iagolast/32/184_2.png) [@IagoLast](https://es.discourse.group/u/IagoLast)\
**Post date:** [December 18, 2019, 4:55pm UTC](https://es.discourse.group/t/object-isempty/166/1 "2019-12-18T16:55:55Z")

</div>

Hi there my idea is adding a new `.isEmtpy()` method similar to the [isFrozen](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Object/isFrozen) to checking if a given object is empty or not.

I think [it's a pretty common issue](https://stackoverflow.com/questions/679915/how-do-i-test-for-an-empty-javascript-object), and most of the utility libraries have something similar:

- [https://api.jquery.com/jQuery.isEmptyObject/](https://api.jquery.com/jQuery.isEmptyObject/)
- [https://lodash.com/docs#isEmpty](https://lodash.com/docs#isEmpty)
- [https://ramdajs.com/docs/#isEmpty](https://ramdajs.com/docs/#isEmpty)

Some test cases examples:

```javascript
expect(isEmpty(undefined)).toBe(true);
  expect(isEmpty(null)).toBe(true);
  expect(isEmpty('')).toBe(true);
  expect(isEmpty({})).toBe(true);
  expect(isEmpty([])).toBe(true);
  expect(isEmpty(new Set())).toBe(true);
  expect(isEmpty(new Map())).toBe(true);
  expect(isEmpty(new WeakMap())).toBe(true);
  expect(isEmpty(new Array())).toBe(true);
  expect(isEmpty(new Float32Array())).toBe(true);

  expect(isEmpty(true)).toBe(false);
  expect(isEmpty(false)).toBe(false);

  expect(isEmpty(0)).toBe(false);
  expect(isEmpty(1)).toBe(false);
  expect(isEmpty(0x10)).toBe(false);

  expect(isEmpty('0')).toBe(false);
  expect(isEmpty('foo')).toBe(false);
  expect(isEmpty([0])).toBe(false);
  expect(isEmpty([1])).toBe(false);

  expect(isEmpty(new Set(['0']))).toBe(false);
  expect(isEmpty(new Set([1]))).toBe(false);

  const weakmap = new WeakMap();
  weakmap.set({}, {});
  expect(isEmpty(weakmap)).toBe(true); // We cannot know if a weakmap is empty.

```

A very naive polyfill:

```javascript
function isEmpty(obj) {
    if (typeof obj == 'boolean') {
        return false;
    }
    if (obj == undefined || obj == null) {
        return true;
    }
    if (typeof obj == 'number') {
        return false;
    }
    if (obj == '') {
        return true;
    }
    if (obj.constructor == Set) {
        return obj.size == 0;
    }
    return Object.entries(obj).length == 0;
}

thx for reading
```

---

<div class="post-metadata">

**Author:** ![ljharb](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/ljharb/32/8_2.png) [@ljharb](https://es.discourse.group/u/ljharb)\
**Post date:** [December 18, 2019, 5:26pm UTC](https://es.discourse.group/t/object-isempty/166/2 "2019-12-18T17:26:15Z")

</div>

Is `Object.assign([], { a: true })` empty, or not empty?

---

<div class="post-metadata">

**Author:** ![IagoLast](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/iagolast/32/184_2.png) [@IagoLast](https://es.discourse.group/u/IagoLast)\
**Post date:** [December 18, 2019, 5:48pm UTC](https://es.discourse.group/t/object-isempty/166/3 "2019-12-18T17:48:43Z")

</div>

Hey, thx for replying!

`Object.assign([], { a: true })` returns an array with an object inside so I guess it should return false bc the Array is not empty.

---

<div class="post-metadata">

**Author:** ![claudepache](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/claudepache/32/236_2.png) [@claudepache](https://es.discourse.group/u/claudepache)\
**Post date:** [December 18, 2019, 6:02pm UTC](https://es.discourse.group/t/object-isempty/166/4 "2019-12-18T18:02:57Z")

</div>

I have a method in my personal library that is roughly equivalent to:

```js
Object.hasNoKey = function (o) {
    return Object.keys(o).length === 0
}

```

The name is carefully chosen in order to make clear what it really means. (It is almost equivalent to jQuery’s `isEmptyObject()`, except maybe wrt inherited properties.)

The problem with a “generic” `isEmpty()` method as defined by Lodash, is that determining the emptiness of an object requires knowledge of its structure, so that it cannot be really generic; e.g., in order to have:

> [@IagoLast](#):
>
> Object.isEmpty(new Set([1])); // false  
> Object.isEmpty(new Set()); // true

it must be known that `new Set(...).size` ought to be checked.

---

<div class="post-metadata">

**Author:** ![IagoLast](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/iagolast/32/184_2.png) [@IagoLast](https://es.discourse.group/u/IagoLast)\
**Post date:** [December 18, 2019, 7:01pm UTC](https://es.discourse.group/t/object-isempty/166/5 "2019-12-18T19:01:21Z")

</div>

you are totally right! But I think there are some ways to fix that, the proposal is very similar to [Object.entries implementation](https://tc39.es/ecma262/#sec-object.entries).

```nohighlight

Object.isEmpty ( O )
When the entries function is called with argument O, the following steps are taken:

Let obj be ? ToObject(O).
Let nameList be ? EnumerableOwnPropertyNames(obj, key+value).
Let arr to be CreateArrayFromList(nameList).
Let size be the length of arr
If size = 0, then
  Return true
Else
  Return false

```

(notice the above pseudocode wont work for numbers, or booleans)

---

<div class="post-metadata">

**Author:** ![ljharb](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/ljharb/32/8_2.png) [@ljharb](https://es.discourse.group/u/ljharb)\
**Post date:** [December 18, 2019, 10:21pm UTC](https://es.discourse.group/t/object-isempty/166/6 "2019-12-18T22:21:31Z")

</div>

No, it returns an empty array with an additional property on it. `Object.keys(x).length` would be 1, but `x.length` would be zero.

What about `Object.create({ a: 1, b: 2 })`? That has zero own keys (so an implementation that counted own keys would say it's empty) but two inherited enumerable ones. If you count inherited and non-enumerable keys, then `{}` has 12 in my current browser. Do you count Symbol properties too, or only string properties? What about a `Map` or a `Set` that's empty, but has extra own enumerable properties?

The question of "what does empty mean" is a very complex one that has a great many potentially valid interpretations. Which one would you suggest the language enshrine, and what about the valid use cases for alternate interpretations?

---

<div class="post-metadata">

**Author:** ![IagoLast](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/iagolast/32/184_2.png) [@IagoLast](https://es.discourse.group/u/IagoLast)\
**Post date:** [December 18, 2019, 11:12pm UTC](https://es.discourse.group/t/object-isempty/166/7 "2019-12-18T23:12:13Z")

</div>

You are totally right. I'm dumb 🤦‍♂️

First of all thx for the feedback, you just blow my mind.

> what does empty mean?

Thats the point! I think we need to reach a consensus there. My current answer would be: something like:

```js
function isEmpty(obj) {
    if (typeof obj == 'boolean') {
        return false;
    }
    if (obj == undefined || obj == null) {
        return true;
    }
    if (typeof obj == 'number') {
        return false;
    }
    if (obj == '') {
        return true;
    }
    if (obj.constructor == Set) {
        return obj.size == 0;
    }
    if (obj.constructor == Map) {
        return obj.size == 0;
    }
    return Object.entries(obj).length == 0;
}

```

(As far as I know we cannot tell anything about WeakMap or WeakSet so isEmpty() will fail/give a wrong answer for that ☹ )

Does it make sense to you to rely only on own keys? I think it could make sense, at least for me since most of the time I'd use this feature to handle data from server responses.

Regarding symbols I don't have enough knowledge to give you a strong answer but I'd love to know your opinion.

Again, thank you very much for replying

---

<div class="post-metadata">

**Author:** ![ljharb](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/ljharb/32/8_2.png) [@ljharb](https://es.discourse.group/u/ljharb)\
**Post date:** [December 18, 2019, 11:48pm UTC](https://es.discourse.group/t/object-isempty/166/8 "2019-12-18T23:48:05Z")

</div>

+0, -0, NaN are not empty? What about negative numbers?

I'd expect `false` to be empty but `true` not to be.

For Set and Map, we'd need to use a robust brand check and not check the constructor (in the absence of a new protocol), what about subclasses that define their own `size` getter semantics?

What about a regex? They have no own enumerable keys, so your algorithm would mark them as empty, but i'd consider an empty regex to be one that can't match anything (like `RegExp.prototype`).

What about a Promise? I'd consider an empty Promise to be one that can't ever resolve, but that's not knowable.

I think that it's entirely possible to bikeshed answers to all of these questions, but I don't know how valuable it would be, since each user/application is likely to have its own concept of "emptiness" combined with "what kinds of objects they want to consider the emptiness of".

---

<div class="post-metadata">

**Author:** ![claudiameadows](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/claudiameadows/32/126_2.png) [@claudiameadows](https://es.discourse.group/u/claudiameadows)\
**Post date:** [December 19, 2019, 11:26pm UTC](https://es.discourse.group/t/object-isempty/166/9 "2019-12-19T23:26:56Z")

</div>

> [@claudepache](#):
>
> I have a method in my personal library that is roughly equivalent to:
> 
> ```javascript
> Object.hasNoKey = function (o) {
> return Object.keys(o).length === 0
> }
> 
> ```

I'd prefer this kind of semantics for an `Object.isEmpty` - it's simple and everyone interprets it as that low-level primitive. It also fits in with the rest:

- `Object.keys(new Map([["a", 1], ["b", 2]]))` returns `[]`, not `["a", "b"]`.
- `Object.values(new Map([["a", 1], ["b", 2]]))` returns `[]`, not `[1, 2]`.
- `Object.entries(new Map([["a", 1], ["b", 2]]))` returns `[]`, not `[["a", 1], ["b", 2]]`.

Better might be just `Object.size(o)`, which returns effectively `Object.keys(o).length`. I've personally have had a couple cases where the length was useful independent of it, and it's easy enough to check `Object.size(o) === 0` or `!Object.size(o)` for `Object.isEmpty(o)`. And in nearly all runtimes, this is a _very_ cheap O(1) access for non-proxies, cheaper than even property access.

---

<div class="post-metadata">

**Author:** ![IagoLast](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/iagolast/32/184_2.png) [@IagoLast](https://es.discourse.group/u/IagoLast)\
**Post date:** [December 20, 2019, 9:53am UTC](https://es.discourse.group/t/object-isempty/166/10 "2019-12-20T09:53:37Z")

</div>

Yes, maybe that kind of semantic is better. As ljharb said the core problem here is each use case will be potentially different and "being emtpy" is a vague concept.

This would be my exagerated use case which I think it's more or less common:

```javascript
const response = parseServerResponse(rawResponse);
if(isEmpty(response)) {
 // Response could be "false", an emtpy array or an emtpy object... 
}
// Handle non emtpy responses

```

IMO most common use case is checking for `{}` this can be a nightmare for new javascript developers!

```javascript
const response = {};

if(!response) {
  // What?! oh wait... is not a falsy value...ok
}

if(response == {}) {
  // Nope? oh, makes sense...object equality yes...yes...
}

if(response.toString() == '{}') {
  // "[object Object]" !? >.<
}

if(JSON.stringify({}) == '{}') {
  // meh...
}

```

The problem with checking keys is there are some weird cases for Map and Set (and probably I'm missing more)

```javascript
function hasNoKeys(obj) {
    if (!obj) {
        return true;
    }
    return Object.keys(obj).length == 0;
}

```

```javascript
  expect(hasNoKeys(-0)).toBe(true);
  expect(hasNoKeys(NaN)).toBe(true);
  expect(hasNoKeys(undefined)).toBe(true);
  expect(hasNoKeys(null)).toBe(true);
  expect(hasNoKeys('')).toBe(true);
  expect(hasNoKeys({})).toBe(true);
  expect(hasNoKeys([])).toBe(true);
  expect(hasNoKeys(new Array())).toBe(true);
  expect(hasNoKeys(new Float32Array())).toBe(true);

  expect(hasNoKeys(true)).toBe(true);
  expect(hasNoKeys(false)).toBe(true);

  expect(hasNoKeys(0)).toBe(true);
  expect(hasNoKeys(1)).toBe(true);
  expect(hasNoKeys(0x10)).toBe(true);

  expect(hasNoKeys('0')).toBe(false);
  expect(hasNoKeys('foo')).toBe(false);
  expect(hasNoKeys([0])).toBe(false);
  expect(hasNoKeys([1])).toBe(false);

  expect(hasNoKeys(new Set())).toBe(true);
  expect(hasNoKeys(new Set(['0']))).toBe(true); // ?
  expect(hasNoKeys(new Set([1]))).toBe(true); // ?

  const map = new Map();
  expect(hasNoKeys(map)).toBe(true); 
  map.set('foo', 'bar');
  expect(hasNoKeys(map)).toBe(true); // ?

  const weakmap = new WeakMap();
  expect(hasNoKeys(weakmap)).toBe(true);
  weakmap.set({}, {});
  expect(hasNoKeys(weakmap)).toBe(true); // ?

  const weakSet = new WeakSet();
  expect(hasNoKeys(weakSet)).toBe(true);
  weakSet.add({});
  expect(hasNoKeys(weakSet)).toBe(true); // ?

```

Maybe a `Object.isVoid` method to check for falsy values, empty arrays and of course empty objects would make sense but after ljharb comments I think this is going to be very hard...

---

<div class="post-metadata">

**Author:** ![kaizhu256](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/kaizhu256/32/342_2.png) [@kaizhu256](https://es.discourse.group/u/kaizhu256)\
**Post date:** [December 20, 2019, 4:28pm UTC](https://es.discourse.group/t/object-isempty/166/11 "2019-12-20T16:28:59Z")

</div>

+1 on Object.isEmpty, and i think people are thinking too hard about unlikely use-cases.

Arrays and non-plain-objects (Maps, Sets, etc) are not realistic use-cases for Object.isEmpty.

the realistic use-case is the one where:

1. user **already knows beforehand** their dealing with a plain-object (or null or undefined)
2. and just wants to see if the plain-object has any enumerable "dictionary" items.

which should be less-messy to spec.

---

<div class="post-metadata">

**Author:** ![claudiameadows](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/claudiameadows/32/126_2.png) [@claudiameadows](https://es.discourse.group/u/claudiameadows)\
**Post date:** [December 24, 2019, 1:17pm UTC](https://es.discourse.group/t/object-isempty/166/12 "2019-12-24T13:17:47Z")

</div>

It's possible to resolve this by just communicating what the intent is. Nobody does `Object.keys(new Map([[1, "1"]]))` expecting it to return `[1]`, for instance. So to me personally, that concern of `Object.isEmpty(new Set([1]))` returning `true` (as `Object.keys(new Set([1])).length` is `0`) is overblown.

---

<div class="post-metadata">

**Author:** ![yuanyan](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/yuanyan/32/472_2.png) [@yuanyan](https://es.discourse.group/u/yuanyan)\
**Post date:** [October 30, 2020, 6:21am UTC](https://es.discourse.group/t/object-isempty/166/13 "2020-10-30T06:21:40Z")

</div>

`"hey".isEmpty` will more naturally?

---

<div class="post-metadata">

**Author:** ![IagoLast](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/iagolast/32/184_2.png) [@IagoLast](https://es.discourse.group/u/IagoLast)\
**Post date:** [October 30, 2020, 11:28am UTC](https://es.discourse.group/t/object-isempty/166/14 "2020-10-30T11:28:06Z")

</div>

I'm just a js developer, not a js expert at all, but I think this proposal cover most of "common" cases an average developer will encounter in their day to day.

## Pseudocode/Polyfill:

The idea would be a recursive approach iterating over all the enumerable properties of the object.  
If at some point `length` or `size` are defined this properties will be used to determine if the object is empty otherwise

```javascript
function isEmpty(object) {
    if (object === undefined) {
        return true;
    }

    if (object === null) {
        return true;
    }

    if ((typeof object.length) === 'number') {
        return object.length === 0;
    }

    if ((typeof object.size) === 'number') {
        return object.size === 0;
    }

    if (Object.getPrototypeOf(object) === Object.prototype) {
        return Object.keys(object).length === 0;
    }

    return isEmpty(Object.getPrototypeOf(object));
}

```

With that we cover the following cases with a behaviour that might be suitable for common use cases:

```javascript
describe('.isEmpty()', () => {
    test('Basic types', () => {
        expect(isEmpty(-0)).toBe(true);
        expect(isEmpty(+0)).toBe(true);
        expect(isEmpty(NaN)).toBe(true);
        expect(isEmpty(undefined)).toBe(true);
        expect(isEmpty(null)).toBe(true);
        expect(isEmpty('')).toBe(true);
        expect(isEmpty({})).toBe(true); // This is the keypoint of all of this!
        expect(isEmpty([])).toBe(true);
        expect(isEmpty(true)).toBe(true);
        expect(isEmpty(false)).toBe(true);
        // Numbers are empty
        expect(isEmpty(1)).toBe(true);
        expect(isEmpty(0)).toBe(true);
        expect(isEmpty(1)).toBe(true);
        expect(isEmpty(0x10)).toBe(true);
        // Strings rely on its length
        expect(isEmpty('')).toBe(true);
        expect(isEmpty('0')).toBe(false);
        expect(isEmpty('foo')).toBe(false);
    });

    test('Complex objects', () => {
        // "Complex" Objects like arrays could be considered empty when its length === 0

        // Array uses array.length
        expect(isEmpty(new Array())).toBe(true);
        expect(isEmpty(new Float32Array())).toBe(true);
        expect(isEmpty([0])).toBe(false);
        expect(isEmpty([1])).toBe(false);

        // // Set relies on size
        expect(isEmpty(new Set())).toBe(true);
        expect(isEmpty(new Set(['0']))).toBe(false);
        expect(isEmpty(new Set([1]))).toBe(false);

        // // Map relies on size
        const map = new Map();
        expect(isEmpty(map)).toBe(true);
        map.set('foo', 'bar');
        expect(isEmpty(map))
    });

    test('Prototypes and stuff', () => {
        expect(isEmpty({ a: 1 })).toBe(false);
        
        expect(isEmpty(Object.create({ a: 1, b: 2 }))).toBe(false);
        
        // length takes precedence
        expect(isEmpty(Object.assign([], { a: true }))).toBe(true); // (?)
    });

    test('Non intuitive cases', () => {
        // Weakmap is always empty (?)
        const weakmap = new WeakMap();
        expect(isEmpty(weakmap)).toBe(true);
        weakmap.set({}, {});
        expect(isEmpty(weakmap)).toBe(true); // (?)

        // Weakset is always empty (?)
        const weakSet = new WeakSet();
        expect(isEmpty(weakSet)).toBe(true);
        weakSet.add({});
        expect(isEmpty(weakSet)).toBe(true); // (?)

        // Dates are empty (?)
        expect(isEmpty(new Date())).toBe(true); // (?)
        expect(isEmpty(Date.UTC(2020, 20, 10))).toBe(true); // (?)
    });

    test('Size and length properties will take priority', () => {
        class EmptyWeakMap extends WeakMap {
            get length() {
                return 0;
            }
        }
        const emptyWeakMap = new EmptyWeakMap();
        expect(isEmpty(emptyWeakMap)).toBe(true);

        class NonEmptyWeakMap extends WeakMap {
            get length() {
                return 10;
            }
        }

        const nonEmptyWeakMap = new NonEmptyWeakMap();
        expect(isEmpty(nonEmptyWeakMap)).toBe(false);

        // Same for prototypes
        expect(isEmpty(Object.create({ size: 0, b: 2 }))).toBe(true);
        expect(isEmpty(Object.create({ length: 0, b: 2 }))).toBe(true);
    });
});

```

with that said I think there should be big community consensus about the expected behavior of this feature.

@ljharb thoughts ?
