JS Howto define function on subobjects - javascript

I have an object containing nested objects, lets say:
var root = {
'areas' : [
{ 'areaName' : 'A1',
'modules' : [ { 'moduleName' : 'M1', moduleType : 'F' },
{ 'moduleName' : 'M2', moduleType : 'F' }] },
{ 'areaName' : A2,
'modules' : [ { 'moduleName' : 'M1', moduelType : 'B' },
{ 'moduleName' : 'M2', moduleType : 'F' }] }
];
}
This is a Json returned from a WS. I want to define a functions that can be called upon modules. Lets say:
root.A1.M2.Foo();
1) Can this be done in a fashionably way? I could iterate over all the objects in a for loop and add the function for each object. But I was looking more in the direction of using a prototype or something alike.
2) Can it be done to define this function so it has knowledge of its position in the hierarchy? So that you can do:
function Foo(){
var module = ... ('this', I assume)
var area = ...
var root = ...
}
3) Bonus-question: Can it be archived to add the function only if a certain condition is met? So that Foo() is only added to modules with moduleType == 'F' and Bar() to modules with moduleType == 'B'
root.A1.M1.Foo(); // good
root.A2.M1.Bar(); // good
root.A2.M2.Bar(); // bad
I do have control about the WS, so returning the object in another format is possible, although not preferable.
Looking forward to your creative solutions!

A good result should be reached using ECMASCRIPT-6 Proxies as described here: https://developer.mozilla.org/it/docs/Web/JavaScript/Reference/Global_Objects/Proxy
unfortunately, the current support of es6 proxies isn't good but almost all vendors are working to make it available as soon as possible. https://kangax.github.io/compat-table/es6/
this is a google polyfill: https://github.com/GoogleChrome/proxy-polyfill

An attempt to get what you want. This goes straigth to point 3, returning a function with dependent name. the reurn value is the path, it could be all other reference to this object or another.
Your point 2 is a bit vague, where should
var module = ... ('this', I assume)
var area = ...
var root = ...
point to?
And another question arised, is the structure fixed? are the properties fixed? ans where should the result point to? To the original object or like this proposal to a different one?
function iter(o, r, key, a) {
key = key || 'root';
a = a || [key];
Object.keys(o).forEach(function (k) {
if (~k.indexOf('Name')) {
a = a.slice();
r[key] = r[key] || {};
r = r[key];
key = o[k];
a.push(o[k]);
return;
}
if (~k.indexOf('Type')) {
r[key] = {};
r[key][{ F: 'Foo', B: 'Bar' }[o[k]]] = function () {
return a;
};
return;
}
if (Array.isArray(o[k])) {
o[k].forEach(function (b) {
iter(b, r, key, a);
});
return;
}
if (typeof o[k] === 'object') {
iter(o[k], r, key, a);
}
});
}
var object = { root: { areas: [{ areaName: 'A1', modules: [{ moduleName: 'M1', moduleType: 'F' }, { moduleName: 'M2', moduleType: 'F' }] }, { areaName: 'A2', modules: [{ moduleName: 'M1', moduleType: 'B' }, { moduleName: 'M2', moduleType: 'F' }] }] } },
result = {};
iter(object, result);
document.write('<pre>' + JSON.stringify(result, 0, 4) + '</pre>');
document.write(result.root.A1.M1.Foo() + '<br>');
document.write(result.root.A1.M2.Foo() + '<br>');
document.write(result.root.A2.M1.Bar() + '<br>');
document.write(result.root.A2.M2.Foo() + '<br>');

1) array.prototype.map() can help you. https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Array/map
var test = [{id : 1}, {id : 2}];
var newTest = test.map(function (i) {i.value = 'a'});
console.log(newTest);
// [{id : 1, value : 'a'}, {id : 2, value : 'a'}]
2 & 3) I don't understand, sorry, language limits

You can think about the object you receive from sockets, like about raw data that must be deserialized [1]. So you can define a factory [2] that accepts this raw data and constructs objects with format and methods that you need. As a bonus this will help you decouple data representation from logic, so in future you can change format of data you sending via network with less effort.
https://en.wikipedia.org/wiki/Serialization
https://en.wikipedia.org/wiki/Factory_(object-oriented_programming)

Related

Determine if array element represents an object or a value

From a JSON object (containing stock data), I want to add certain elements to an array (in Google Sheets script editor):
var quote = JSON.parse(response.getContentText());
// Example of what quote object looks like:
{
"quoteSummary": {
"result": [
{
"Profile": {
"sector": "Technology",
"website": "www.test.com"
},
"Owners": [
{
"name": "Eric",
"age": "28"
},
{
"name": "Susan",
"age": "44"
}
],
"Profit": 100,
"Assets": 7000
}
]
}
}
Here is my current approach to read only some specific values:
var arr = [];
arr.push(quote.quoteSummary.result[0].Profile.sector); // Technology
arr.push(quote.quoteSummary.result[0].Owners[1].name); // Susan
arr.push(quote.quoteSummary.result[0].Profit); // 100
But since there are many specific properties to read, I'd like to use a loop:
var quote = JSON.parse(response.getContentText());
var arr = [];
var el = [
['Profile', 'sector'],
['Owners[1]', 'name'],
['Profit']
];
for (i = 0; i < el.length; i++)
{
if (quote.quoteSummary.result[0][el[i][0]][el[i][1]] !== undefined)
{
arr.push(quote.quoteSummary.result[0][el[i][0]][el[i][1]].value);
}
}
/*
Expected output (if I would loop through arr):
Technology
Susan
100
*/
The point is that different stocks, will have different properties. So el might define some non-existing elements or properties. Assume (in a bit of an other way of defining el -- as I wrote, I'm plexible here.. perhaps the paths are the easiest):
var el = [
'Profile.website',
'Profile.name',
'Assets'
]
/*
Expected output:
www.test.com
<----- "name" doesn't exist!
7000
Notice that in this example, there is no property "name" in Profile,
so I'd like to add an empty element to arr
*/
But this does not work. What is a generic loop that accomplishes what I'm trying to do here? The array defining what I want can also be constructed differently if that helps. But the point is that I don't end up with a script like:
arr.push(quote.quoteSummary.result[0].Profile.something);
arr.push(quote.quoteSummary.result[0].Profile.something);
arr.push(quote.quoteSummary.result[0].Profile.something);
arr.push(quote.quoteSummary.result[0].Profile.something);
arr.push(quote.quoteSummary.result[0].Profile.something);
arr.push(quote.quoteSummary.result[0].Profile.something);
arr.push(quote.quoteSummary.result[0].Profile.something);
arr.push(quote.quoteSummary.result[0].Profile.something);
arr.push(quote.quoteSummary.result[0].Profile.something);
arr.push(quote.quoteSummary.result[0].Profile.something);
arr.push(quote.quoteSummary.result[0].Profile.something);
I recommend you use variable-length chains of property names. Each name in a given chain represents a deeper property. You can "dive" into an object through an arbitrary number of property names with code like this:
let dive = (obj, propertyNames) => {
for (let pn of propertyNames) obj = obj[pn];
return obj;
};
Now you can say:
let dive = (obj, propertyNames) => {
for (let pn of propertyNames) obj = obj[pn];
return obj;
};
let quote = {
quoteSummary: {
result: [
{
Profile: {
sector: 'Technology',
website: 'www.test.com'
},
Owners: [
{
name: 'Eric',
age: '28'
},
{
name: 'Susan',
age: '44'
}
],
Profit: 100,
Assets: 7000
}
]
}
};
// Here are the "variable-length property chains":
let el = [
[ 'Profile', 'sector' ],
[ 'Owners', 1, 'name' ],
[ 'Profit' ]
];
// Here's how to combine `el`, `dive`, and your `quote` data to get a result:
let arr = el.map(propertyNames => dive(quote.quoteSummary.result[0], propertyNames));
console.log(arr);
You could even replace dive with Array.prototype.reduce, if you'd like to stay functional but avoid the function definition:
dive(someObj, propertyNames);
is equivalent to
propertyNames.reduce((obj, propName) => obj[propName], someObj);
Note the above code all assumes that a property exists for each term in the property chain (except the final property name, which may resolve to undefined without causing any errors). If some cases may have the, e.g., Profile key undefined or null you'll need to write some kind of if (propertyDoesntExist) / else statement which describes how to deal with missing properties.
For example you could modify dive to handle non-existent properties:
let dive = (obj, propertyNames, valueIfMissing=null) => {
for (let pn of propertyNames) {
// Intentional use of loose `==` operator!
if (obj == null) return valueIfMissing;
obj = obj[pn];
}
return obj;
};
This means that:
dive({ a: 1, b: 2, c: 3 }, [ 'd' ]) === null;
But we can substitute any default return value:
dive({ a: 1, b: 2, c: 3 }, [ 'd' ], 'ddd') === 'ddd';
Note this works at any depth:
dive({ a: { a: 1 }, b: { b: 2 }, c: { c: 3 }, d: null }, [ 'd', 'd' ]) === null;
dive({ a: { a: 1 }, b: { b: 2 }, c: { c: 3 }, d: null }, [ 'd', 'd' ], 'ddd') === 'ddd';
You should be able to do this:
if(y.value){
arr.push(y.value);
} else {
arr.push(y);
}
However, this will break if y.value happens to be something like 0, false, etc.
If this is the case you can do this:
if(y.hasOwnProperty("value")){
arr.push(y.value);
} else {
arr.push(y);
}
Based on the comment:
if (quote.quoteSummary.result[0][el[i][0]].hasOwnProperty("value")) {
arr.push(quote.quoteSummary.result[0][el[i][0]].value);
} else {
arr.push(quote.quoteSummary.result[0][el[i][0]]);
}

Why should we used a square bracket when using a calculated property for an object? [duplicate]

In JavaScript, I've created an object like so:
var data = {
'PropertyA': 1,
'PropertyB': 2,
'PropertyC': 3
};
Is it possible to add further properties to this object after its initial creation if the properties name is not determined until run time? i.e.
var propName = 'Property' + someUserInput
//imagine someUserInput was 'Z', how can I now add a 'PropertyZ' property to
//my object?
Yes.
var data = {
'PropertyA': 1,
'PropertyB': 2,
'PropertyC': 3
};
data["PropertyD"] = 4;
// dialog box with 4 in it
alert(data.PropertyD);
alert(data["PropertyD"]);
ES6 for the win!
const b = 'B';
const c = 'C';
const data = {
a: true,
[b]: true, // dynamic property
[`interpolated-${c}`]: true, // dynamic property + interpolation
[`${b}-${c}`]: true
}
If you log data you get this:
{
a: true,
B: true,
interpolated-C: true,
B-C: true
}
This makes use of the new Computed Property syntax and Template Literals.
Yes it is possible. Assuming:
var data = {
'PropertyA': 1,
'PropertyB': 2,
'PropertyC': 3
};
var propertyName = "someProperty";
var propertyValue = "someValue";
Either:
data[propertyName] = propertyValue;
or
eval("data." + propertyName + " = '" + propertyValue + "'");
The first method is preferred. eval() has the obvious security concerns if you're using values supplied by the user so don't use it if you can avoid it but it's worth knowing it exists and what it can do.
You can reference this with:
alert(data.someProperty);
or
data(data["someProperty"]);
or
alert(data[propertyName]);
ES6 introduces computed property names, which allows you to do
let a = 'key'
let myObj = {[a]: 10};
// output will be {key:10}
I know that the question is answered perfectly, but I also found another way to add new properties and wanted to share it with you:
You can use the function Object.defineProperty()
Found on Mozilla Developer Network
Example:
var o = {}; // Creates a new object
// Example of an object property added with defineProperty with a data property descriptor
Object.defineProperty(o, "a", {value : 37,
writable : true,
enumerable : true,
configurable : true});
// 'a' property exists in the o object and its value is 37
// Example of an object property added with defineProperty with an accessor property descriptor
var bValue;
Object.defineProperty(o, "b", {get : function(){ return bValue; },
set : function(newValue){ bValue = newValue; },
enumerable : true,
configurable : true});
o.b = 38;
// 'b' property exists in the o object and its value is 38
// The value of o.b is now always identical to bValue, unless o.b is redefined
// You cannot try to mix both :
Object.defineProperty(o, "conflict", { value: 0x9f91102,
get: function() { return 0xdeadbeef; } });
// throws a TypeError: value appears only in data descriptors, get appears only in accessor descriptors
Here, using your notation:
var data = {
'PropertyA': 1,
'PropertyB': 2,
'PropertyC': 3
};
var propName = 'Property' + someUserInput
//imagine someUserInput was 'Z', how can I now add a 'PropertyZ' property to
//my object?
data[propName] = 'Some New Property value'
You can add as many more properties as you like simply by using the dot notation:
var data = {
var1:'somevalue'
}
data.newAttribute = 'newvalue'
or:
data[newattribute] = somevalue
for dynamic keys.
in addition to all the previous answers, and in case you're wondering how we're going to write dynamic property names in the Future using Computed Property Names ( ECMAScript 6 ), here's how:
var person = "John Doe";
var personId = "person_" + new Date().getTime();
var personIndex = {
[ personId ]: person
// ^ computed property name
};
personIndex[ personId ]; // "John Doe"
reference: Understanding ECMAScript 6 - Nickolas Zakas
Just an addition to abeing's answer above. You can define a function to encapsulate the complexity of defineProperty as mentioned below.
var defineProp = function ( obj, key, value ){
var config = {
value: value,
writable: true,
enumerable: true,
configurable: true
};
Object.defineProperty( obj, key, config );
};
//Call the method to add properties to any object
defineProp( data, "PropertyA", 1 );
defineProp( data, "PropertyB", 2 );
defineProp( data, "PropertyC", 3 );
reference: http://addyosmani.com/resources/essentialjsdesignpatterns/book/#constructorpatternjavascript
I know there are several answers to this post already, but I haven't seen one wherein there are multiple properties and they are within an array. And this solution by the way is for ES6.
For illustration, let's say we have an array named person with objects inside:
let Person = [{id:1, Name: "John"}, {id:2, Name: "Susan"}, {id:3, Name: "Jet"}]
So, you can add a property with corresponding value. Let's say we want to add a Language with a default value of EN.
Person.map((obj)=>({...obj,['Language']:"EN"}))
The Person array now would become like this:
Person = [{id:1, Name: "John", Language:"EN"},
{id:2, Name: "Susan", Language:"EN"}, {id:3, Name: "Jet", Language:"EN"}]
It can be useful if mixed new property add in runtime:
data = { ...data, newPropery: value}
However, spread operator use shallow copy but here we assign data to itself so should lose nothing
You can add properties dynamically using some of the options below:
In you example:
var data = {
'PropertyA': 1,
'PropertyB': 2,
'PropertyC': 3
};
You can define a property with a dynamic value in the next two ways:
data.key = value;
or
data['key'] = value;
Even more..if your key is also dynamic you can define using the Object class with:
Object.defineProperty(data, key, withValue(value));
where data is your object, key is the variable to store the key name and value is the variable to store the value.
I hope this helps!
I was looking for a solution where I can use dynamic key-names inside the object declaration (without using ES6 features like ... or [key]: value)
Here's what I came up with:
var obj = (obj = {}, obj[field] = 123, obj)
It looks a little bit complex at first, but it's really simple. We use the Comma Operator to run three commands in a row:
obj = {}: creates a new object and assigns it to the variable obj
obj[field] = 123: adds a computed property name to obj
obj: use the obj variable as the result of the parentheses/comma list
This syntax can be used inside a function parameter without the requirement to explictely declare the obj variable:
// The test function to see the result.
function showObject(obj) {
console.log(obj);
}
// My dynamic field name.
var field = "myDynamicField";
// Call the function with our dynamic object.
showObject( (obj = {}, obj[field] = 123, obj) );
/*
Output:
{
"myDynamicField": true
}
*/
Some variations
"strict mode" workaround:
The above code does not work in strict mode because the variable "obj" is not declared.
// This gives the same result, but declares the global variable `this.obj`!
showObject( (this.obj = {}, obj[field] = 123, obj) );
ES2015 code using computed property names in initializer:
// Works in most browsers, same result as the other functions.
showObject( {[field] = 123} );
This solution works in all modern browsers (but not in IE, if I need to mention that)
Super hacky way using JSON.parse():
// Create a JSON string that is parsed instantly. Not recommended in most cases.
showObject( JSON.parse( '{"' + field +'":123}') );
// read: showObject( JSON.parse( '{"myDynamicfield":123}') );
Allows special characters in keys
Note that you can also use spaces and other special characters inside computed property names (and also in JSON.parse).
var field = 'my dynamic field :)';
showObject( {[field] = 123} );
// result: { "my dynamic field :)": 123 }
Those fields cannot be accessed using a dot (obj.my dynamic field :) is obviously syntactically invalid), but only via the bracket-notation, i.e., obj['my dynamic field :)'] returns 123
The simplest and most portable way is.
var varFieldName = "good";
var ob = {};
Object.defineProperty(ob, varFieldName , { value: "Fresh Value" });
Based on #abeing answer!
Be careful while adding a property to the existing object using .(dot) method.
(.dot) method of adding a property to the object should only be used if you know the 'key' beforehand otherwise use the [bracket] method.
Example:
var data = {
'Property1': 1
};
// Two methods of adding a new property [ key (Property4), value (4) ] to the
// existing object (data)
data['Property2'] = 2; // bracket method
data.Property3 = 3; // dot method
console.log(data); // { Property1: 1, Property2: 2, Property3: 3 }
// But if 'key' of a property is unknown and will be found / calculated
// dynamically then use only [bracket] method not a dot method
var key;
for(var i = 4; i < 6; ++i) {
key = 'Property' + i; // Key - dynamically calculated
data[key] = i; // CORRECT !!!!
}
console.log(data);
// { Property1: 1, Property2: 2, Property3: 3, Property4: 4, Property5: 5 }
for(var i = 6; i < 2000; ++i) {
key = 'Property' + i; // Key - dynamically calculated
data.key = i; // WRONG !!!!!
}
console.log(data);
// { Property1: 1, Property2: 2, Property3: 3,
// Property4: 4, Property5: 5, key: 1999 }
Note the problem in the end of console log -
'key: 1999' instead of Property6: 6, Property7: 7,.........,Property1999: 1999. So the best way of adding dynamically created property is the [bracket] method.
A nice way to access from dynamic string names that contain objects (for example object.subobject.property)
function ReadValue(varname)
{
var v=varname.split(".");
var o=window;
if(!v.length)
return undefined;
for(var i=0;i<v.length-1;i++)
o=o[v[i]];
return o[v[v.length-1]];
}
function AssignValue(varname,value)
{
var v=varname.split(".");
var o=window;
if(!v.length)
return;
for(var i=0;i<v.length-1;i++)
o=o[v[i]];
o[v[v.length-1]]=value;
}
Example:
ReadValue("object.subobject.property");
WriteValue("object.subobject.property",5);
eval works for read value, but write value is a bit harder.
A more advanced version (Create subclasses if they dont exists, and allows objects instead of global variables)
function ReadValue(varname,o=window)
{
if(typeof(varname)==="undefined" || typeof(o)==="undefined" || o===null)
return undefined;
var v=varname.split(".");
if(!v.length)
return undefined;
for(var i=0;i<v.length-1;i++)
{
if(o[v[i]]===null || typeof(o[v[i]])==="undefined")
o[v[i]]={};
o=o[v[i]];
}
if(typeof(o[v[v.length-1]])==="undefined")
return undefined;
else
return o[v[v.length-1]];
}
function AssignValue(varname,value,o=window)
{
if(typeof(varname)==="undefined" || typeof(o)==="undefined" || o===null)
return;
var v=varname.split(".");
if(!v.length)
return;
for(var i=0;i<v.length-1;i++)
{
if(o[v[i]]===null || typeof(o[v[i]])==="undefined")
o[v[i]]={};
o=o[v[i]];
}
o[v[v.length-1]]=value;
}
Example:
ReadValue("object.subobject.property",o);
WriteValue("object.subobject.property",5,o);
This is the same that o.object.subobject.property
Here's how I solved the problem.
var obj = {
};
var field = "someouter.someinner.someValue";
var value = 123;
function _addField( obj, field, value )
{
// split the field into tokens
var tokens = field.split( '.' );
// if there's more than one token, this field is an object
if( tokens.length > 1 )
{
var subObj = tokens[0];
// define the object
if( obj[ subObj ] !== undefined ) obj[ subObj ] = {};
// call addfield again on the embedded object
var firstDot = field.indexOf( '.' );
_addField( obj[ subObj ], field.substr( firstDot + 1 ), value );
}
else
{
// no embedded objects, just field assignment
obj[ field ] = value;
}
}
_addField( obj, field, value );
_addField(obj, 'simpleString', 'string');
console.log( JSON.stringify( obj, null, 2 ) );
Generates the following object:
{
"someouter": {
"someinner": {
"someValue": 123
}
},
"simpleString": "string"
}
Yes it is possible. I have achieved using below implementation. for that I am getting array in response which I want in an object as list of attributes.
response = {
"equityMonths": [
{
"id": 1,
"month": "JANUARY",
"isEligible": false
},
{
"id": 2,
"month": "FEBRUARY",
"isEligible": true
},
{
"id": 3,
"month": "MARCH",
"isEligible": false
},
{
"id": 4,
"month": "APRIL",
"isEligible": true
},
{
"id": 5,
"month": "MAY",
"isEligible": false
},
{
"id": 6,
"month": "JUNE",
"isEligible": true
},
{
"id": 7,
"month": "JULY",
"isEligible": true
},
{
"id": 8,
"month": "AUGUST",
"isEligible": false
},
{
"id": 9,
"month": "SEPTEMBER",
"isEligible": true
},
{
"id": 10,
"month": "OCTOBER",
"isEligible": false
},
{
"id": 11,
"month": "NOVEMBER",
"isEligible": true
},
{
"id": 12,
"month": "DECEMBER",
"isEligible": false
}
]
}
here, I want equityMonths as an object and Jan to Dec it's key and isEligible as value. for that we have to use Object class's defineProperty() method which allows to add dynamic property into objects.
code for adding property dynamically to the object.
let equityMonth = new Object();
response.equityMonths.forEach(element => {
Object.defineProperty(equityMonth, element['month'], {
value: element['isEligible'],
writable: true,
enumerable: true,
configurable: true
});
});
console.log("DATA : " + JSON.stringify(equityMonth));
in above code we have array of equityMonths which we have converted as property into the object.
output:
DATA : {"JANUARY":false,"FEBRUARY":true,"MARCH":false,"APRIL":true,"MAY":false,"JUNE":true,"JULY":true,"AUGUST":false,"SEPTEMBER":true,"OCTOBER":false,"NOVEMBER":true,"DECEMBER":false}
A perfect easy way
var data = {
'PropertyA': 1,
'PropertyB': 2,
'PropertyC': 3
};
var newProperty = 'getThisFromUser';
data[newProperty] = 4;
console.log(data);
If you want to apply it on an array of data (ES6/TS version)
const data = [
{ 'PropertyA': 1, 'PropertyB': 2, 'PropertyC': 3 },
{ 'PropertyA': 11, 'PropertyB': 22, 'PropertyC': 33 }
];
const newProperty = 'getThisFromUser';
data.map( (d) => d[newProperty] = 4 );
console.log(data);
Definitely. Think of it as a dictionary or associative array. You can add to it at any point.

React - Filter JSON array if key exists [duplicate]

I have an array of objects and I'm wondering the best way to search it. Given the below example how can I search for name = "Joe" and age < 30? Is there anything jQuery can help with or do I have to brute force this search myself?
var names = new Array();
var object = { name : "Joe", age:20, email: "joe#hotmail.com"};
names.push(object);
object = { name : "Mike", age:50, email: "mike#hotmail.com"};
names.push(object);
object = { name : "Joe", age:45, email: "mike#hotmail.com"};
names.push(object);
A modern solution with Array.prototype.filter():
const found_names = names.filter(v => v.name === "Joe" && v.age < 30);
Or if you still use jQuery, you may use jQuery.grep():
var found_names = $.grep(names, function(v) {
return v.name === "Joe" && v.age < 30;
});
You can do this very easily with the [].filter method:
var filterednames = names.filter(function(obj) {
return (obj.name === "Joe") && (obj.age < 30);
});
You can learn more about it on this MDN page.
You could utilize jQuery.filter() function to return elements from a subset of the matching elements.
var names = [
{ name : "Joe", age:20, email: "joe#hotmail.com"},
{ name : "Mike", age:50, email: "mike#hotmail.com"},
{ name : "Joe", age:45, email: "mike#hotmail.com"}
];
var filteredNames = $(names).filter(function( idx ) {
return names[idx].name === "Joe" && names[idx].age < 30;
});
$(filteredNames).each(function(){
$('#output').append(this.name);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="output"/>
var nameList = [
{name:'x', age:20, email:'x#email.com'},
{name:'y', age:60, email:'y#email.com'},
{name:'Joe', age:22, email:'joe#email.com'},
{name:'Abc', age:40, email:'abc#email.com'}
];
var filteredValue = nameList.filter(function (item) {
return item.name == "Joe" && item.age < 30;
});
//To See Output Result as Array
console.log(JSON.stringify(filteredValue));
You can simply use javascript :)
For those who want to filter from an array of objects using any key:
function filterItems(items, searchVal) {
return items.filter((item) => Object.values(item).includes(searchVal));
}
let data = [
{ "name": "apple", "type": "fruit", "id": 123234 },
{ "name": "cat", "type": "animal", "id": 98989 },
{ "name": "something", "type": "other", "id": 656565 }]
console.log("Filtered by name: ", filterItems(data, "apple"));
console.log("Filtered by type: ", filterItems(data, "animal"));
console.log("Filtered by id: ", filterItems(data, 656565));
filter from an array of the JSON objects:**
var names = [{
name: "Joe",
age: 20,
email: "joe#hotmail.com"
},
{
name: "Mike",
age: 50,
email: "mike#hotmail.com"
},
{
name: "Joe",
age: 45,
email: "mike#hotmail.com"
}
];
const res = _.filter(names, (name) => {
return name.name == "Joe" && name.age < 30;
});
console.log(res);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.5/lodash.js"></script>
So quick question. What if you have two arrays of objects and you would like to 'align' these object arrays so that you can make sure each array's objects are in the order as the other array's? What if you don't know what keys and values any of the objects inside of the arrays contains... Much less what order they're even in?
So you need a 'WildCard Expression' for your [].filter, [].map, etc. How do you get a wild card expression?
var jux = (function(){
'use strict';
function wildExp(obj){
var keysCrude = Object.keys(obj),
keysA = ('a["' + keysCrude.join('"], a["') + '"]').split(', '),
keysB = ('b["' + keysCrude.join('"], b["') + '"]').split(', '),
keys = [].concat(keysA, keysB)
.sort(function(a, b){ return a.substring(1, a.length) > b.substring(1, b.length); });
var exp = keys.join('').split(']b').join('] > b').split(']a').join('] || a');
return exp;
}
return {
sort: wildExp
};
})();
var sortKeys = {
k: 'v',
key: 'val',
n: 'p',
name: 'param'
};
var objArray = [
{
k: 'z',
key: 'g',
n: 'a',
name: 'b'
},
{
k: 'y',
key: 'h',
n: 'b',
name: 't'
},
{
k: 'x',
key: 'o',
n: 'a',
name: 'c'
}
];
var exp = jux.sort(sortKeys);
console.log('#juxSort Expression:', exp);
console.log('#juxSort:', objArray.sort(function(a, b){
return eval(exp);
}));
You can also use this function over an iteration for each object to create a better collective expression for all of the keys in each of your objects, and then filter your array that way.
This is a small snippet from the API Juxtapose which I have almost complete, which does this, object equality with exemptions, object unities, and array condensation. If these are things you need or want for your project please comment and I'll make the lib accessible sooner than later.
Hope this helps! Happy coding :)
The most straightforward and readable approach will be the usage of native javascript filter method.
Native javaScript filter takes a declarative approach in filtering array elements. Since it is a method defined on Array.prototype, it iterates on a provided array and invokes a callback on it. This callback, which acts as our filtering function, takes three parameters:
element — the current item in the array being iterated over
index — the index or location of the current element in the array that is being iterated over
array — the original array that the filter method was applied on
Let’s use this filter method in an example. Note that the filter can be applied on any sort of array. In this example, we are going to filter an array of objects based on an object property.
An example of filtering an array of objects based on object properties could look something like this:
// Please do not hate me for bashing on pizza and burgers.
// and FYI, I totally made up the healthMetric param :)
let foods = [
{ type: "pizza", healthMetric: 25 },
{ type: "burger", healthMetric: 10 },
{ type: "salad", healthMetric: 60 },
{ type: "apple", healthMetric: 82 }
];
let isHealthy = food => food.healthMetric >= 50;
const result = foods.filter(isHealthy);
console.log(result.map(food => food.type));
// Result: ['salad', 'apple']
To learn more about filtering arrays in functions and yo build your own filtering, check out this article:
https://medium.com/better-programming/build-your-own-filter-e88ba0dcbfae

Remove duplicate object from array javascript [duplicate]

I have this kind of array:
var foo = [ { "a" : "1" }, { "b" : "2" }, { "a" : "1" } ];
I'd like to filter it to have:
var bar = [ { "a" : "1" }, { "b" : "2" }];
I tried using _.uniq, but I guess because { "a" : "1" } is not equal to itself, it doesn't work. Is there any way to provide underscore uniq with an overriden equals function?
.uniq/.unique accepts a callback
var list = [{a:1,b:5},{a:1,c:5},{a:2},{a:3},{a:4},{a:3},{a:2}];
var uniqueList = _.uniq(list, function(item, key, a) {
return item.a;
});
// uniqueList = [Object {a=1, b=5}, Object {a=2}, Object {a=3}, Object {a=4}]
Notes:
Callback return value used for comparison
First comparison object with unique return value used as unique
underscorejs.org demonstrates no callback usage
lodash.com shows usage
Another example :
using the callback to extract car makes, colors from a list
If you're looking to remove duplicates based on an id you could do something like this:
var res = [
{id: 1, content: 'heeey'},
{id: 2, content: 'woah'},
{id: 1, content:'foo'},
{id: 1, content: 'heeey'},
];
var uniques = _.map(_.groupBy(res,function(doc){
return doc.id;
}),function(grouped){
return grouped[0];
});
//uniques
//[{id: 1, content: 'heeey'},{id: 2, content: 'woah'}]
Implementation of Shiplu's answer.
var foo = [ { "a" : "1" }, { "b" : "2" }, { "a" : "1" } ];
var x = _.uniq( _.collect( foo, function( x ){
return JSON.stringify( x );
}));
console.log( x ); // returns [ { "a" : "1" }, { "b" : "2" } ]
When I have an attribute id, this is my preffered way in underscore:
var x = [{i:2}, {i:2, x:42}, {i:4}, {i:3}];
_.chain(x).indexBy("i").values().value();
// > [{i:2, x:42}, {i:4}, {i:3}]
Using underscore unique lib following is working for me, I m making list unique on the based of _id then returning String value of _id:
var uniqueEntities = _.uniq(entities, function (item, key, a) {
return item._id.toString();
});
Here is a simple solution, which uses a deep object comparison to check for duplicates (without resorting to converting to JSON, which is inefficient and hacky)
var newArr = _.filter(oldArr, function (element, index) {
// tests if the element has a duplicate in the rest of the array
for(index += 1; index < oldArr.length; index += 1) {
if (_.isEqual(element, oldArr[index])) {
return false;
}
}
return true;
});
It filters out all elements if they have a duplicate later in the array - such that the last duplicate element is kept.
The testing for a duplicate uses _.isEqual which performs an optimised deep comparison between the two objects see the underscore isEqual documentation for more info.
edit: updated to use _.filter which is a cleaner approach
The lodash 4.6.1 docs have this as an example for object key equality:
_.uniqWith(objects, _.isEqual);
https://lodash.com/docs#uniqWith
Try iterator function
For example you can return first element
x = [['a',1],['b',2],['a',1]]
_.uniq(x,false,function(i){
return i[0] //'a','b'
})
=> [['a',1],['b',2]]
here's my solution (coffeescript) :
_.mixin
deepUniq: (coll) ->
result = []
remove_first_el_duplicates = (coll2) ->
rest = _.rest(coll2)
first = _.first(coll2)
result.push first
equalsFirst = (el) -> _.isEqual(el,first)
newColl = _.reject rest, equalsFirst
unless _.isEmpty newColl
remove_first_el_duplicates newColl
remove_first_el_duplicates(coll)
result
example:
_.deepUniq([ {a:1,b:12}, [ 2, 1, 2, 1 ], [ 1, 2, 1, 2 ],[ 2, 1, 2, 1 ], {a:1,b:12} ])
//=> [ { a: 1, b: 12 }, [ 2, 1, 2, 1 ], [ 1, 2, 1, 2 ] ]
with underscore i had to use String() in the iteratee function
function isUniq(item) {
return String(item.user);
}
var myUniqArray = _.uniq(myArray, isUniq);
I wanted to solve this simple solution in a straightforward way of writing, with a little bit of a pain of computational expenses... but isn't it a trivial solution with a minimum variable definition, is it?
function uniq(ArrayObjects){
var out = []
ArrayObjects.map(obj => {
if(_.every(out, outobj => !_.isEqual(obj, outobj))) out.push(obj)
})
return out
}
var foo = [ { "a" : "1" }, { "b" : "2" }, { "a" : "1" } ];
var bar = _.map(_.groupBy(foo, function (f) {
return JSON.stringify(f);
}), function (gr) {
return gr[0];
}
);
Lets break this down. First lets group the array items by their stringified value
var grouped = _.groupBy(foo, function (f) {
return JSON.stringify(f);
});
grouped looks like:
{
'{ "a" : "1" }' = [ { "a" : "1" } { "a" : "1" } ],
'{ "b" : "2" }' = [ { "b" : "2" } ]
}
Then lets grab the first element from each group
var bar = _.map(grouped, function(gr)
return gr[0];
});
bar looks like:
[ { "a" : "1" }, { "b" : "2" } ]
Put it all together:
var foo = [ { "a" : "1" }, { "b" : "2" }, { "a" : "1" } ];
var bar = _.map(_.groupBy(foo, function (f) {
return JSON.stringify(f);
}), function (gr) {
return gr[0];
}
);
You can do it in a shorthand as:
_.uniq(foo, 'a')

Sort multidimensional array of objects with a variable depth in Javascript

So I've recently come across a problem I can't seem to wrap my head around.
Let's say I've defined an array of objects in javascript, and want the user to be able to choose what value to sort that array by.
I have no problem sorting the array when I know the depth, as it would be something along the lines of
array = array.sort(function (a, b) {
return b["foo"]["bar"] - a["foo"]["bar"];
});
but I don't exactly know how to go about doing this when the depth is unknown. I've attempted putting the keys in a string and using eval(), but that does not seem to work.
I've set up a quick example on JSFiddle to better demonstrate what I mean
http://jsfiddle.net/DakotaSv/c35bj02w/2/
If anyone could think of a solution, I'd be grateful!
(Thanks to PatrickD, here is the working JSFiddle for anyone who may find it useful!)
Here is a working solution. It uses ES6-Syntax, but this should not be a problem:
'use strict'
var users = [
{'Name' : 'John', 'Attributes' : {'Age' : 5, 'Height' : 1.5, 'Clothes' : {'Shirts' : 5, 'Pants' : 8}}},
{'Name' : 'Andrew', 'Attributes' : {'Age' : 9, 'Height' : 1.8, 'Clothes' : {'Shirts' : 2, 'Pants' : 5}}},
{'Name' : 'Lucifer', 'Attributes' : {'Age' : 11, 'Height' : 1.3, 'Clothes' : {'Shirts' : 9, 'Pants' : 4}}}
];
function sort(valuePath, array){
let path = valuePath.split('.')
return array.sort((a, b) => {
return getValue(b,path) - getValue(a,path)
});
function getValue(obj, path){
path.forEach(path => obj = obj[path])
return obj;
}
}
console.log(sort('Attributes.Height', users))
console.log(sort('Attributes.Clothes.Shirts', users))
The output is correct.
Maybe this is a solution for variable sorting scheme. The sort attribute is just given, like ['Attributes', 'Height']. This uses the properties Attributes and Height.
It features a temporary storage for faster sorting.
function sort(a, by) {
return a.map(function (el, i) {
return {
index: i,
value: by.reduce(function (obj, property) { return obj[property]; }, el)
};
}).sort(function (a, b) {
return a.value - b.value;
}).map(function (el) {
return a[el.index];
});
}
var users = [{ 'Name': 'John', 'Attributes': { 'Age': 5, 'Height': 1.5, 'Clothes': { 'Shirts': 5, 'Pants': 8 } } }, { 'Name': 'Andrew', 'Attributes': { 'Age': 9, 'Height': 1.8, 'Clothes': { 'Shirts': 2, 'Pants': 5 } } }, { 'Name': 'Lucifer', 'Attributes': { 'Age': 11, 'Height': 1.3, 'Clothes': { 'Shirts': 9, 'Pants': 4 } } }];
document.write('<pre>' + JSON.stringify(sort(users, ['Attributes', 'Height']), 0, 4) + '</pre>');
document.write('<pre>' + JSON.stringify(sort(users, ['Attributes', 'Clothes', 'Shirts']), 0, 4) + '</pre>');
If I understand your question correctly, you want the user to chose which attribute to sort the array by.
Looking at your fiddle I think that what you need is accessing an attribute specified by the user, fortunately it's possible to specify a variable inside the brackets. Something like:
var obj = {name: 'john'}
var attr = "name";
console.log(obj[attr]); // prints 'john'
Here's your modified fiddle: https://jsfiddle.net/9s5bnfh5/1/
You would need:
a way to represent how to access the sort key given an object
a function that, given a sort key representation and an object, queries the object and produces the key
The manner of representation can be arbitrarily selected, so let's say we decide to encode the access someObject.foo.bar as the string "foo.bar". Then the key producing function would be (adapted from my answer here):
function produceKey(target, path) {
var parts = path.split('.');
while(parts.length) {
var branch = parts.shift();
if (typeof target[branch] === 'undefined') {
return undefined;
}
target = target[branch];
}
return target;
}
which you could then use as:
function produceKey(target, path) {
var parts = path.split('.');
while(parts.length) {
var branch = parts.shift();
if (typeof target[branch] === 'undefined') {
return undefined;
}
target = target[branch];
}
return target;
}
var obj = { foo: { bar: 1, baz: 2 }, arr: [1, 2, 3] };
$(function() {
$("#trigger").click(function() {
$(".result").text(JSON.stringify(produceKey(obj, $("input").val())));
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Targe object:
<pre>
{ foo: { bar: 1, baz: 2 }, arr: [1, 2, 3] }
</pre>
Property path: <input name="path" />
<button id="trigger">Produce value</button>
<div class="result"></div>
The cleanest way of doing this is passing a callback function to your sort algorithm that is executed to retrieve the value to compare on from the object:
function cbSort(toSort, callback)
{
return toSort.sort(function(a, b)
{
return callback(a) - callback(b);
}
}
// usage
var data = [ { x: 1, y: 2 }, {x: 3, y: 1}];
var sorted = cbSort(data, function(item) {
return item.x; // make this as complex as you like
});

Categories

Resources