6

Given

var arr = [1,2,true,4,{"abc":123},6,7,{"def":456},9,[10]]

we can filter number items within array arr using Number constructor

var res = arr.filter(Number); // [1, 2, true, 4, 6, 7, 9, Array[1]]

are true and [10] expected in resulting array ? If we substitute false for true at arr

var arr = [1,2,false,4,{"abc":123},6,7,{"def":456},9,[10]] 
var res = arr.filter(Number) // [1, 2, 4, 6, 7, 9, Array[1]]

using Array.isArray

var res = arr.filter(Array.isArray) // [Array[1]]

String

var res = arr.filter(String) // [1, 2, true, 4, Object, 6, 7, Object, 9, Array[1]]

If we want to filter items within arr that are object, at indexes 4 , 7 and we try

var res = arr.filter(Object) // [1, 2, true, 4, Object, 6, 7, Object, 9, Array[1]]

Although we would prefer to simply call arr.filter(Object), we could pass a function call; trying different properties of Object so that we can eventually find a property or method that we could use as a function or constructor to pass to as the pattern arr.filter(/* method, constructor, other approach */) to return the filtered results matching the object, or even property name or value of the object within the input array.

We start, innocently enough, by checking if the item in the array has a constructor having name equal to "Object"

 var res = arr.filter(function(prop) {
  return prop.constructor.name === "Object"
 }) // [Object, Object]

though when we add an object to arr; e.g.;

 var c = Object.create(null); arr.push(c); 

 var res = arr.filter(function(prop) {
   return prop.constructor.name === "Object"
 }) // `Uncaught TypeError: Cannot read property 'name' of undefined`

as c prototype and constructor are undefined. Although we are certain that this will not return expected results

var n = arr.filter(Object.hasOwnProperty, "abc"); // [1, 2]

at least an error was not returned; let us continue

var n = arr.filter(function(prop, val) {
          return prop.hasOwnProperty(this.valueOf())
        }, "abc"); // [Object abc: 123__proto__: Object]

the expected results are returned; though we are trying to use

var n = arr.filter(/* function reference */, this /* optional parameters passed */)

to

  1. filter an array for Object : {} objects; even if the object does not have a defined prototype or constructor; optionally converting JSON string "{"abc":123}" to object; though we have not reached this far, yet;

  2. pass a property name to .filter(callback, this) pattern where this serves as property name, or value of object; or utilize an approach using filter.bind , .call or .apply or other method to filter an object from the input array - without using full

    .filter(function(prop, value) {})

    pattern. How can we coerce the Object.hasOwnProperty() call into a pattern similar to

    .filter(Object.hasOwnProperty, "abc")

?

Mentioning .call, .bind and .apply after searching for a similar Question and finding JS Array.prototype.filter on prototype method . Though not certain how to implement approaches described in filtering both objects and objects having specific properties as described above.

Note, Question can also be resolved by a destructuring , or other es-6, es-7 approach, providing comparable or, even stricter results, when compared to .filter(). That is, use .filter() without

   function(prop, value) {

   }

pattern. Returning objects; that is Object , {} ; and objects filtered by property ; objects filtered by property value.


Questions:

  1. How to filter objects with or without Object prototype or constructor within in an array passed to Array.prototype.filter() without using an anonymous function callbackpattern ?

  2. How to filter specific objects within an array passed to Array.prototype.filter() by passing property name or value to match object without using anonymous function callback pattern ?

Community
  • 1
  • 1
guest271314
  • 1
  • 10
  • 82
  • 156
  • 1
    why don't you want to use anonymous functions? for me it sounds like _I don't want to use var in JavaScript_ – smnbbrv Mar 26 '16 at 07:18
  • @smnbbrv Question seeks to determine if expected results can be returned without an anonymous function pattern utilizing `javascript` – guest271314 Mar 26 '16 at 07:26
  • 1
    @guest271314 can you explore this `arr.filter(Object.getNotifier);` its output is an error but it does check for what you want. – gurvinder372 Mar 26 '16 at 09:58
  • @gurvinder372 _"its output is an error"_ Yes. Without `.filter()` `Object.getNotifier(arr)` logs `'Object.observe' is deprecated and will be removed in M50, around April 2016. See https://www.chromestatus.com/features/6147094632988672 for more details.` ; prospective usage ? – guest271314 Mar 26 '16 at 10:06
  • 2
    I'm not sure you understand what `Number` and `String` do as arguments to the `filter` function? They are *makeNumber* and *makeString*, not *isNumber* and *isString* - and don't make much sense to be passed as a predicate. – Bergi Mar 26 '16 at 10:12
  • @guest271314 No I meant if you pass a method like this `arr.filter(Object.isExtensible);` it throws an error if any item of array is not an object. Is it possible to catch the exception and continue with rest of the items in the array? – gurvinder372 Mar 26 '16 at 10:14
  • 2
    @guest271314 `[0,1, 2, "a"].filter(Number); // [1, 2]` since 0 is a falsey value. – gurvinder372 Mar 26 '16 at 10:26
  • @gurvinder372 Updated – guest271314 Mar 26 '16 at 10:33
  • 3
    SO is not a blog or discussion group. Please do not turn your post into a running commentary on your state of mind after reading the latest answers and comments. If you have a response to a comment. respond with another comment. –  Mar 26 '16 at 13:04
  • I'm not sure you have any idea what is actually happening when you pass a constructor as a callback to `filter`, or for that matter what the callback even does. Passing `Object` as the callback is the equivalant of specifying the function `function(e) { return Object(e); }`, which is **completely** different from asking if the element is an object, and the same for the other cases you experimented with. In any, this question could and should have been boiled down to about two lines. –  Mar 26 '16 at 13:11
  • Your question about *how filter an array for Object* is precisely identical to the question "how to determine if a value is an object", for which there are many, many answers here are SO, the most basic one being `Object.prototype.toString.call(x) === "[object Object]"`. –  Mar 26 '16 at 13:13
  • 1
    This has nothing whatsoever to do with destructuring; you might want to remove that unfortunate part of your post. Nor is it clear what you mean by calling filter without a callback--using a callback to determine which elements to filter in and out is the entire DNA of `filter`. Unclear what pattern you are referring to when you say `function(prop, value) { }` pattern. –  Mar 26 '16 at 13:29
  • @Bergi Ultimately trying to do something like `var res = JSON.parse(JSON.stringify(arr.filter(x => Object.prototype.toString.call(x) === '[object Object]' /* or, however object type is checked */), ["abc"] /* if we want only an object that has specific property, pass in property name*/))` – guest271314 Mar 26 '16 at 16:38
  • @Bergi without having to remove the empty object from results; actually without using explicit arrow function or writing actual text of callback `function(){}`; but rather using built-in methods ; e.g; `arr.filter(Object.prototype.toString, "abc" /* somehow pass "abc" as property to check, after filtering objects in array*/)`; preferably reduced to the form of `var [abc] = arr // [{"abc":123}]` or `var {abc} = arr // {"abc":123}` . Trying to determine if this is possible using `.filter()` and built-in methods , `.bind()`, `.call()`, other; without explicitly composing a callback function – guest271314 Mar 26 '16 at 16:40
  • 1
    @guest271314: Now your actual question sounds a lot like [One-liner to take some properties from object in ES6](http://stackoverflow.com/q/25553910/1048572). No, I'm pretty sure there's no way to do that only by combining builtin functions - and if there was, it would be quite unreadable. – Bergi Mar 26 '16 at 19:18

6 Answers6

2

How to filter objects with or without Object prototype or constructor within in an array passed to Array.prototype.filter() without using an anonymous function callbackpattern ?

As per spec

callbackfn should be a function that accepts three arguments and returns a value that is coercible to the Boolean value true or false

Number object (function's constructor) does return NaN for bad Number conversion but String and Object constructors don't return a false value (yes, filter(Number) also filters out 0)

var arr = [0,1,2,true,4,{"abc":123},6,7,{"def":456},9,[10]];
arr.filter(Number); //outputs [1, 2, true, 4, 6, 7, 9, Array[1]]

You can create a customer function OBJ,

function OBJ(value,index,arr){ return typeof value === "object" && !Array.isArray(value) }

or Arrays are also welcome in the resultset then remove the Array.isArray check

function OBJ(value,index,arr){ return typeof value === "object" }

when used with

arr.filter(OBJ); //outputs [{"abc":123},{"def":456}]
gurvinder372
  • 61,170
  • 7
  • 61
  • 75
  • what of object has a `length` property? – smnbbrv Mar 26 '16 at 07:25
  • is `var distance = { length: 1000 }` `Array`? – smnbbrv Mar 26 '16 at 07:27
  • @guest271314 Not sure what you mean since `typeof []` returns `"object"` – gurvinder372 Mar 26 '16 at 07:40
  • @gurvinder372 Yes. `arr.filter(OBJ) // [Object, Object, Array[1]] ` ; at Answer `arr.filter(OBJ); //outputs [{"abc":123},{"def":456}]` ? – guest271314 Mar 26 '16 at 07:41
  • @guest271314 so you want Arrays to be included in the resultset? then yes you need to go by second option `function OBJ(value,index,arr){ return typeof value === "object" }` – gurvinder372 Mar 26 '16 at 07:43
  • @gurvinder372 No. Arrays should not be included within results. Have you read Question ? Was trying to convey that `typeof` returns `"object"` for arrays. `arr.filter(OBJ)`, with current `OBJ`at Answer does not return `//outputs [{"abc":123},{"def":456}]` – guest271314 Mar 26 '16 at 07:43
  • @guest271314 yes, check this fiddle https://jsfiddle.net/avpyrsLm/ isn't this giving the output you are expecting? – gurvinder372 Mar 26 '16 at 07:46
  • @gurvinder372 _"isn't this giving the output you are expecting?"_ Yes. `function OBJ(value,index,arr){ return typeof value === "object" }` does not. Though this is not gist of actual Question. Perhaps, re-read original Question ? – guest271314 Mar 26 '16 at 07:51
  • @guest271314 yes I did read your original question, perhaps you are missing the gist of my answer. I did mentioned that `filter` doesn't work in a way you are expecting. It filter out falsey values are applying the constructor (you are providing) to it. And Object constructor doesn't generate falsey values let alone filtering objects from an array. – gurvinder372 Mar 26 '16 at 08:02
  • @gurvinder372 `Object.hasOwnProperty` returns `true` , `false` – guest271314 Mar 26 '16 at 08:05
  • 1
    @guest271314 this behaviour of Object.hasOwnProperty with filter is strange, have posted this related question http://stackoverflow.com/questions/36233124/array-filter-returns-strange-results – gurvinder372 Mar 26 '16 at 08:21
2

There is no real way to do it safely without creating your own function. Additionally it is very complicated because the definition of Object is too broad.

Let's start with the following:

var types = ['1', 2, true, null, undefined, [], {}, new Date()];

and run the following:

types.map((e) => typeof e);
// ["string", "number", "boolean", "object", "undefined", "object", "object", "object"]

Do you think of null of as an Object? I don't think so. Do you think of an Array as of an Object, because the Array is an instance of Object? I am not sure as well.

What you can try is the following:

types.map(Object.isExtensible);
// [false, false, false, false, false, true, true, true]

This excludes the null from the result but still the array is present here. The Date Object is here as well as any other Object with any prototype, e.g. new Boolean() will also be an Object. Additionally the object could be frozen and this won't be returned as an Object here as well.

So the both examples here successfully demonstrate that the definition of Object is too broad and it cannot be really handled in a useful way.

smnbbrv
  • 19,108
  • 9
  • 62
  • 100
  • 1
    There is a quite clear definition what constitutes an *object* in JS: everything for which `Object(o) === o` yields `true`. – Bergi Mar 26 '16 at 12:48
  • 1
    *There is no real way to do it safely without creating your own function.* should omit the word "safely" or replace it with the word "at all". Also, "very complicated" is probably an exaggeration. People have been figuring out how to test for object-ness (by different criteria) for years and the topic is pretty well exhausted. –  Mar 26 '16 at 13:35
1

You seem to want to filter an array for elements with a certain type. Pass an appropriate function to filter:

array.filter(istype("String"))

You just need to write istype now:

function istype(type) {
  return function(x) {
    return Object.prototype.toString.call(x) === '[object ' + type + ']';
  }
}

You seem to have thought you could filter for numbers by saying filter(Number) etc. But that will not work. Number is just another function, which tries to turn something into a number (not check if it's a number). Then, filter filters the array depending on whether the result is truthy or falsy. Number will produce a truthy value for any non-zero number obviously, and true. For a string, or an object, or pretty much anything else, it will return NaN, which is falsy, with odd exceptions such as returning 0 for [] or an all-blank string.

Same with string. String is just another function, which tries to turn something into a string. Then, filter filters the array depending on whether the result is truthy or falsy. String will produce a truthy value for almost anything other than a non-empty string.

This has nothing whatsoever to do with destructuring; why would you think it does? You might want to remove that unfortunate part of your post. Nor is it clear what you mean by "calling filter without a callback"--using a callback to determine which elements to filter in and out is the entire DNA of filter. It is also unclear what pattern you are referring to when you say function(prop, value) { } pattern.

At the end of your question, you ask two specific questions:

How to filter objects with or without Object prototype or constructor within in an array passed to Array.prototype.filter() without using an anonymous function callbackpattern ?

You filter objects from an input array by providing a function which determines if a particular element is an object. That is not what the object prototype or constructor Object is, so that won't help you. You have to write a little function to pass to filter, that's how it works. It could be anonymous, or it could be defined elsewhere and passed in

How to filter specific objects within an array passed to Array.prototype.filter() by passing property name or value to match object without using anonymous function callback pattern ?

What do you mean by "passing property name or value to match object"? Do you mean, filter out elements which are missing a particular property name or value? Then write a function to do that. There is no built-in function for this purpose, if that is what are looking for.

  • The Question originated from attempts to determine if it is possible to filter objects from an array; Question is primarily concerning objects, not string or number types 1) without explicitly writing `function(x) {//perform check}` and using existing built-in methods; for example we could use `.filter(Array.isArray)` to filter array from input array ; 2) since we know we can use `return Object.prototype.toString.call(x) === '[object Object]';` , how do we pass this to `.filter()` to act on `x` using similar pattern; e.g., `.filter(Object.prototype.toString, "[object Object]")` ? – guest271314 Mar 26 '16 at 15:53
  • Further, if we can filter object by somehow passing a reference to an built-in function, can we utilize, `.bind` or `thisArg` option at `.filter()`, not so much to set `this`, but to pass in, for example, a property name to have ability to both 3) filter objects from input array ; and 4) filter specific object from array ; 4) both without actually defining a function outside of `.filter()` - but using existing built-in methods ? For example `var arr = [0, [], {"abc":123}]` ; we want to be able to filter `{"abc":123}` by both type and property name; – guest271314 Mar 26 '16 at 16:00
  • e.g., 5) `arr.filter(RegExp.prototype.test.bind(Object.prototype.toString.bind(/* how do we bind here*/, "[object Object]")), "abc")` check if item is object - > check property passed as `thisArg` then call test. That piece is only an example to convey what premise of Question is. – guest271314 Mar 26 '16 at 16:04
1

Without passing a callback function, you can instead pass in a regex by using the RegExp.prototype.test method and binding a regex

var arr = [1,2,true,4,{"abc":123},6,7,{"def":456},9,[10]]

var res = arr.filter(RegExp.prototype.test.bind(/\[object Object\]/));

console.log(res)

This would match any string containing [object Object] as well, but it seems highly unlikely that a string would contain those exact words, unless you have made a mistake and included stringified objects in your array.

adeneo
  • 293,187
  • 26
  • 361
  • 361
  • Produces exception when `Object.create(null)` occurs in the array. Also excludes `[10]`, `x=>x`, `new Date()`, `new Set()`, `new Number(2)`, ... – trincot Nov 07 '16 at 11:18
  • @trincot - I was trying to leave out the array, functions, dates, sets, numbers, and other things that aren't just a plain object, hence the check for `[object Object]`, so I'd say it was pretty close. `Object.create(null)` isn't a real "plain" object either, as it doesn't inherit from Object, but it would be preferable if it didn't throw, but that one is hard to fix. – adeneo Nov 07 '16 at 11:26
  • @adeneo `var a = []; a.toString = () => '[object Object]'; ('' + a) === '[object Object]';` http://stackoverflow.com/a/40456709/ – guest271314 Nov 07 '16 at 15:59
  • @adeneo How to first call `JSON.stringify()` for ability to use `RegExp` `/^\{?.+\}$/`? – guest271314 Nov 07 '16 at 16:26
  • @guest271314 - As I've said, I don't think there is a way to indentify an object with certainty, prototypes can be changed, what something stringifies to can be changed, and with so many new methods that let you construct the strangest objects, it's almost impossible, and even more so without a lengthy callback function where one can compare all sorts of things. I think this solution is the one that just passes in a native method to `filter`, that comes the closest to indetifying a plain object, even if it does fail on strings that are the same and altered arrays etc. – adeneo Nov 07 '16 at 18:29
  • @adeneo `res = arr.filter(o => Object.getPrototypeOf(o) === Object.prototype)` filters plain objects `var arr = [1,2,true,4,{"abc":123},6,7,{"def":456},9,[10], Object.create(null), new Map(), Object.create({})]`. The `JSON.stringify()`, `/^\{?.+\}$/.test` approach could as well, though may still need to adjust for string `"{}"`, which did not consider when initially composed. Trying to find a way to pass only `Object.getPrototypeOf`, perhaps by setting `this` to `Object.prototype`, though arrow function does not change `thisArg`. Will check again. – guest271314 Nov 07 '16 at 20:16
  • @adeneo Used `RegExp.prototype.test.bind()` pattern to meet requirement. Substituted `RegExp` `/^\{?.+\}$/` for `/\[object Object\]/` and called `JSON.stringify()` on object before calling `.filter()`. Not optimal as `.map()` is used twice, though meets requirement of Question to not explicitly use `function(element, index, array){}` at `callback` of `.filter()` – guest271314 Nov 15 '16 at 17:53
1

In ES6 the following would do it for the example values you have listed:

arr.filter(Object.isExtensible)

Obviously, this will exclude objects that have been marked non-extensible, by a call to Object.freeze, Object.seal, or Object.preventExtensions. Unless you plan to use those, I believe this does the job.

var arr = [
    /* primitives: */
    2, true, "str", null, undefined, NaN, 
    /* objects */
    new Number(2), {a:1}, Object.create(null), [10], x=>x, new Date(), new Set()
];

var objects = arr.filter(Object.isExtensible);

console.log(objects);
trincot
  • 211,288
  • 25
  • 175
  • 211
  • `[10]` should not be matched. Original Question intent was to match plain objects. Had not considered `Date` or `Set`. What is `x=>x`? – guest271314 Nov 07 '16 at 16:03
  • Ah, `x=>x` is `function function`; though should not be matched. – guest271314 Nov 07 '16 at 16:28
  • I actually already thought of this, and suggested it in the comments [here](http://stackoverflow.com/questions/40455880/what-is-the-briefest-javascript-necessary-to-filter-objects-from-an-array), but it doesn't do what the OP wants, as it returns true for any object *(that is extensible of course)*, not just plain objects. – adeneo Nov 07 '16 at 18:25
  • So what if you want to look for an object with the property `length`? Will `[10]` not be matched? What if a function has other properties assigned to it (which is sometimes done to store static variables on a constructor)? What if an object is made Array-like by adding methods and making it iterable? ... Does an object stop to be a *plain* object when you decorate it with methods? When does an object become a *non-*-object? The point is that all these things *are* objects and to make a distinction is almost artificial and a grey line of separation. – trincot Nov 07 '16 at 18:31
  • See also [`this`](http://stackoverflow.com/a/40457385/5459839) answer to your similar question, which also identifies *all* objects as objects, as I think it should be -- other definitions will easily become ambiguous. – trincot Nov 07 '16 at 19:02
  • The linked approach did not return expected result. Will have to dig out the tests did, do not have access to that os currently; though if recollect correctly there was an issue with result; if you are referring to `Object(value) === value`. Though `Object.getPrototypeOf(o) === Object.prototype` did return expected result, that is,excluded arrays, strings. `Map()`, `Object.create(null)`, `Object.create({})`, result being only plain objects. Though, that is not what was asking at that particular Question – guest271314 Nov 07 '16 at 20:11
  • What is your definition of plain object? An object without methods? – trincot Nov 07 '16 at 20:25
  • Note, am novice at `javascript` as to exacting terms, am not entirely certain about precise terminologies. Object literal, `{}`, or plain object `{}` being the same; if `Object.create({})` or `Object.create({undefined:undefined})`, `Object.create({null:null})`, actually returned `{key:value}` that should also be included. Though that might only further confuse the requirement of the present Question, as did not include `Object.create()` examples at OP. Methods are not relevant; `{}` or `{method:function(){}}` are equivalent. Requirement is to return only object literal, plain objects. – guest271314 Nov 07 '16 at 20:35
  • Object literal cannot be a requirement, as that is just a notation. The same object can be created without any literal involved: `o = new Object(); o.val = 1;`, or from a string: `JSON.parse('{ "val": 1 }')` or a constructor: `function Person() { this.key = 1 }; o = new Person()`, ...etc, etc. The thing is, that arrays are descendants of Object also, and so are Set, Map, etc. You could create them from scratch, starting with an object literal. There is no black and white. When an object is a *plain* object seems ambiguous to me. – trincot Nov 07 '16 at 20:51
  • @trincot Ok. Prefaced last comment with state of awareness of correct terminologies. Given `function Person() { this.key = 1 }; o = new Person() ; var arr = [1,2,true,4,{"abc":123},6,7,{"def":456},9,[10], Object.create(null), new Map(), Object.create({}), JSON.parse('{"val":1}'), new Object({ghi:456}), o]` the result is four objects. Which is expected, here. `Map()` is not part of results. Why do not `Object.create()` objects meet condition using `Object.getPrototypeOf()`? The requirement is to exclude arrays. – guest271314 Nov 07 '16 at 23:14
  • Generally in javascript, a "plain object" is an object created using `{}` or `new Object`, anything else is generally not a "plain object", but these days there are many new ways to create objects, and even `JSON.parse` could create an object, but then it would be created by parsing `{}`, so same same. The "plain object" description has been around for many years, for instance jQuery has had it's own "type" internally, named plainObject, and even a [custom method](http://api.jquery.com/jquery.isplainobject/) to check for plain objects. – adeneo Nov 08 '16 at 00:32
  • So if I create an object with `{}` and add to it all the capabilities that an array has (all methods, all iterations possibilities, all symbols, ...), it is a plain object? Quite fuzzy if you ask me, because arrays are internally created like that; they are derived from Object. – trincot Nov 08 '16 at 09:11
  • @trincot See new Answer. – guest271314 Nov 15 '16 at 18:10
0

The closest have been able to reach to requirement so far, also matches Object.create(null)

var res = []; for (let p of arr) /^\{?.+\}$/.test(JSON.stringify(p)) && res.push(p)
guest271314
  • 1
  • 10
  • 82
  • 156