1

In JavaScript, there are objects that can contain other objects. Arrays, sets, maps, and objects are a couple of these. These objects can be split into two groups: Keyed collections (values accessed via keys ('foo', 'bar')) and indexed collections (values accessed via indexes (0, 1, 2, 3, ...)).

I’d like to write a function that determines whether the object passed to it is a keyed collection, indexed collection, or neither.

At first, I tried using Array.isArray to determine if an object was indexed, but this returned false for instances of arguments, HTMLCollection and NodeList. I then tried checking for a length property, but that returned false positives for String and Function.

Surely this problem has been solved already, right?

MindfulMinun
  • 15
  • 1
  • 5
  • 1
    Arrays and Objects in JS are the same key collection. So the question is - what do you want to achive? If you need to iterate over smth - check for of statement. – Drag13 Aug 28 '18 at 18:39
  • 3
    This is almost certainly an [XY Problem](http://xyproblem.info/). How many times do you create a structure to hold data but later in code you don't know what type of structure you created and need a function to tell you? – gforce301 Aug 28 '18 at 18:53
  • 1
    This is practically impossible. How would you differentiate between, say, `var obj = { 0: 'a', 1: 'b', length: 2, foo: 'bar' }` and `var obj = ['a', 'b']; obj.foo = 'bar';`? What about the case of sparse arrays? – ibrahim mahrir Aug 28 '18 at 19:05
  • 1
    if you need to check if you can iterate over smth - check this answer https://stackoverflow.com/questions/18884249/checking-whether-something-is-iterable – Drag13 Aug 28 '18 at 19:08
  • 1
    @ibrahimmahrir This is the problem I'm trying to solve. How do I differentiate between the two? – MindfulMinun Nov 16 '18 at 19:28

1 Answers1

-1

I believe I have the answer for you. This is a copy of an Answer I wrote elsewhere on this site. Please let me know how it worked for you.

The following is a common typeof hack which is somewhat problematic::

const type = obj => Object.prototype.toString.call(obj);

type("abc");// [object String]
type(123);// [object Number]// What's with all the objects?
type([]);// [object Array]
type({});// [object Object]
type(Object.create(null));// [object Object]
type(-1/0);// [object Number] Not exactly a true number
type(NaN);// [object Number] WTF?

As you can see there are a few problems with it. It always returns two types wrapped in brackets with the first always an "object". This makes the first type useless information if it is always returned. Secondly it is somehat limited in what it distinguishes. It cannot tell us if an object was created as a literal (plain) or with Object.create() which would require the keyword "new" when called. It also falesly calls infinity and NaN a number.

I wish to share a better typeof function that fixes all of those things. It works for all primitives including symbols, anomalies (errors, undefined, null, and NaN), most common cases of native objects and functions (Array, Map, Object, Function, Math, Date, Promise, and many more), and it can even detect between user made objects (identified as Plain) and DOM elements (identified as HTML). It should work for all modern and somewhat older browsers. It is broken down into several functions to make the code more user friendly:

const isDOM = obj => (obj.nodeType && !isPlain(obj)) ? true : false;
const isPlain = obj => obj ? obj.constructor === {}.constructor : false;
const sanString = str => str.replace(/[^a-zA-Z ]/g, "");
const isNaN = obj => (Object.is(obj, NaN) === true) ? true : false;

function objType(obj){
    if(obj === undefined) return undefined;
    if(obj === Infinity) return Infinity;
    if(obj === -Infinity) return -Infinity;
    if(isDOM(obj)) return 'HTML';
    let str = Object.prototype.toString.call(obj);
    if(str === '[object Object]' && isPlain(obj)) return 'Plain';
    str = sanString(str).split(' ');
    if(str[1] === 'Number' && isNaN(obj)) return NaN;
    return str[1];}
}

Use like this:

objType(null);// Null
objType(undefined);// undefined
objType("abc");// String
objType(123);// Number
objType([]);// Array
objType({});// Plain not [object Object]
objType(Object.create(null));// Object is what we want
objType(document.body);// HTML
objType(-1/0);// -Infinity
objType(NaN);// NaN
Jules Manson
  • 214
  • 2
  • 13
  • 1
    Hi @Jules, if you believe that a question needs an identical answer to another question, then the question can be flagged as a duplicate. If however you think the question is different, then you should write a specific answer instead of copying a generic one. – Didier L Oct 01 '18 at 18:15
  • @didier-l You are right. I don't know what I was thinking. Also now that I look at my solution a second time I see it has a lot more problems than I originally thought might occur. – Jules Manson Oct 08 '18 at 20:00