compare object value with length of array in an object with javascript - javascript

EDIT: SOLVED See final solution below
EDIT: The value names are not the same
object 1
var obj1 = {
val1a : 4,
val2a : 3 ,
val3a : 7
}
object 2 with arrays
var obj2 = {
val1b : [one, two, three],
val2b : [oneA, twoA, threeA],
val3b : [oneB]
}
I am trying to do is the following
if(obj1.val1a === obj2.val1b.length){
// code
}
but I do not want it to be so specific. Is there a way to loop over each object and return the values of obj2 that do not match obj1
SOLUTION with underScore
function checkData(obj1, obj2) {
result = []
var keys_obj1 = Object.keys( obj1)
var keys_obj2 = Object.keys( obj2)
_.each(keys_obj1, function(num, i){
if(obj1[keys_obj1[i]].length !== obj2[keys_obj2[i]]) {
result.push(keys_obj1[i]);
}
})
return result;
}

The best way to do this is if both objects have the same name (for a given pair), then using The For/In Loop iterate over one of them and return the value of both, and then compare.
Remade the fiddle using Object.keys to make an array of keys for both objects, now it works even when the keys aren't the same (following the object index)
var obj1 = {
val1a : 4,
val2a : 3 ,
val3a : 7
}
var obj2 = {
val1b : ['one', 'two', 'three'],
val2b : ['oneA', 'twoA', 'threeA'],
val3b : ['oneB']
}
var keys_obj1 = Object.keys( obj1 )
var keys_obj2 = Object.keys( obj2 )
for (i = 0; i < keys_obj1.length; i++) {
console.log(keys_obj1[i]+'-'+obj1[keys_obj1[i]])
console.log(keys_obj2[i]+'-'+obj2[keys_obj2[i]].length);
if(obj1[keys_obj1[i]] === obj2[keys_obj2[i]].length) {
//match
}
}
console output :
"val1a-4"
"val1b-3"
"val2a-3"
"val2b-3"
"val3a-7"
"val3b-1"

If your model is not the definitive one, you could use an array of object like :
var object = [
{table : [one, two, three], length : 3},
{table : [one, two, three], length : 4},
... ];
and compare values using :
object[i].table.length === object[i].length
It's a little bit different from your model
but i hope it may helps.

Quite long-winded, but the only way I could think to do it:
// for each object you are going to...
function pullData(obj) {
var out = {};
var token;
// grab the keys
var keys = Object.keys(obj).map(function (el) {
// token is the character at the end of the key
// the key that is returned is the key without the token
token = el.slice(-1)
return el.slice(0, 4);
});
// for each key, add it to the output object
for (var i = 0, l = keys.length; i < l; i++) {
out[keys[i]] = obj[keys[i] + token]
}
// return an object containing the data and the token
return {data: out, token: token};
}
// take both the output from both objects being parsed
function isNotMatch(obj1, obj2) {
var out = [];
// loop over the first object using the now identical keys
for (var p in obj1.data) {
// check the values and lengths
// if they're not the same push the key and the token as a string
// to the output array
if (obj1.data[p] !== obj2.data[p].length) {
out.push(p + obj2.token);
}
}
// return the array of non-matches
return out;
}
isNotMatch(pullData(obj1), pullData(obj2));
DEMO

UPDATED:
Maybe something like this. You would have to make sure the property names are the same:
var obj1 = {
val1a : 4,
val2a : 3 ,
val3a : 7
};
var obj2 = {
val1a : ['one', 'two', 'three'],
val2a : ['oneA', 'twoA', 'threeA'],
val3a : ['oneB']
};
for(var name in obj1) {
if(obj1[name] === obj2[name].length) {
alert("match");
}
}

Related

Js how to set an item at a nested index in an empty array [duplicate]

I hope someone can help me with this Javascript.
I have an Object called "Settings" and I would like to write a function that adds new settings to that object.
The new setting's name and value are provided as strings. The string giving the setting's name is then split by the underscores into an array. The new setting should get added to the existing "Settings" object by creating new nested objects with the names given by each part of the array, except the last part which should be a string giving the setting's value. I should then be able to refer to the setting and e.g. alert its value. I can do this in a static way like this...
var Settings = {};
var newSettingName = "Modules_Video_Plugin";
var newSettingValue = "JWPlayer";
var newSettingNameArray = newSettingName.split("_");
Settings[newSettingNameArray[0]] = {};
Settings[newSettingNameArray[0]][newSettingNameArray[1]] = {};
Settings[newSettingNameArray[0]][newSettingNameArray[1]][newSettingNameArray[2]] = newSettingValue;
alert(Settings.Modules.Mediaplayers.Video.Plugin);
... the part that creates the nested objects is doing this ...
Settings["Modules"] = {};
Settings["Modules"]["Video"] = {};
Settings["Modules"]["Video"]["Plugin"] = "JWPlayer";
However, as the number of parts that make up the setting name can vary, e.g. a newSettingName could be "Modules_Floorplan_Image_Src", I'd like to do this dynamically using a function such as...
createSetting (newSettingNameArray, newSettingValue);
function createSetting(setting, value) {
// code to create new setting goes here
}
Can anyone help me work out how to do this dynamically?
I presume there has to be a for...loop in there to itterate through the array, but I haven't been able to work out a way to create the nested objects.
If you've got this far thanks very much for taking the time to read even if you can't help.
Put in a function, short and fast (no recursion).
var createNestedObject = function( base, names ) {
for( var i = 0; i < names.length; i++ ) {
base = base[ names[i] ] = base[ names[i] ] || {};
}
};
// Usage:
createNestedObject( window, ["shapes", "triangle", "points"] );
// Now window.shapes.triangle.points is an empty object, ready to be used.
It skips already existing parts of the hierarchy. Useful if you are not sure whether the hierarchy was already created.
Or:
A fancier version where you can directly assign the value to the last object in the hierarchy, and you can chain function calls because it returns the last object.
// Function: createNestedObject( base, names[, value] )
// base: the object on which to create the hierarchy
// names: an array of strings contaning the names of the objects
// value (optional): if given, will be the last object in the hierarchy
// Returns: the last object in the hierarchy
var createNestedObject = function( base, names, value ) {
// If a value is given, remove the last name and keep it for later:
var lastName = arguments.length === 3 ? names.pop() : false;
// Walk the hierarchy, creating new objects where needed.
// If the lastName was removed, then the last object is not set yet:
for( var i = 0; i < names.length; i++ ) {
base = base[ names[i] ] = base[ names[i] ] || {};
}
// If a value was given, set it to the last name:
if( lastName ) base = base[ lastName ] = value;
// Return the last object in the hierarchy:
return base;
};
// Usages:
createNestedObject( window, ["shapes", "circle"] );
// Now window.shapes.circle is an empty object, ready to be used.
var obj = {}; // Works with any object other that window too
createNestedObject( obj, ["shapes", "rectangle", "width"], 300 );
// Now we have: obj.shapes.rectangle.width === 300
createNestedObject( obj, "shapes.rectangle.height".split('.'), 400 );
// Now we have: obj.shapes.rectangle.height === 400
Note: if your hierarchy needs to be built from values other that standard objects (ie. not {}), see also TimDog's answer below.
Edit: uses regular loops instead of for...in loops. It's safer in cases where a library modifies the Array prototype.
function assign(obj, keyPath, value) {
lastKeyIndex = keyPath.length-1;
for (var i = 0; i < lastKeyIndex; ++ i) {
key = keyPath[i];
if (!(key in obj)){
obj[key] = {}
}
obj = obj[key];
}
obj[keyPath[lastKeyIndex]] = value;
}
Usage:
var settings = {};
assign(settings, ['Modules', 'Video', 'Plugin'], 'JWPlayer');
My ES2015 solution. Keeps existing values.
const set = (obj, path, val) => {
const keys = path.split('.');
const lastKey = keys.pop();
const lastObj = keys.reduce((obj, key) =>
obj[key] = obj[key] || {},
obj);
lastObj[lastKey] = val;
};
Example:
const obj = {'a': {'prop': {'that': 'exists'}}};
set(obj, 'a.very.deep.prop', 'value');
console.log(JSON.stringify(obj));
// {"a":{"prop":{"that":"exists"},"very":{"deep":{"prop":"value"}}}}
Using ES6 is shorten. Set your path into an array.
first, you have to reverse the array, to start filling the object.
let obj = ['a','b','c'] // {a:{b:{c:{}}}
obj.reverse();
const nestedObject = obj.reduce((prev, current) => (
{[current]:{...prev}}
), {});
Another recursive solution:
var nest = function(obj, keys, v) {
if (keys.length === 1) {
obj[keys[0]] = v;
} else {
var key = keys.shift();
obj[key] = nest(typeof obj[key] === 'undefined' ? {} : obj[key], keys, v);
}
return obj;
};
Example usage:
var dog = {bark: {sound: 'bark!'}};
nest(dog, ['bark', 'loudness'], 66);
nest(dog, ['woff', 'sound'], 'woff!');
console.log(dog); // {bark: {loudness: 66, sound: "bark!"}, woff: {sound: "woff!"}}
I love this ES6 immutable way to set certain value on nested field:
const setValueToField = (fields, value) => {
const reducer = (acc, item, index, arr) => ({ [item]: index + 1 < arr.length ? acc : value });
return fields.reduceRight(reducer, {});
};
And then use it with creating your target object.
const targetObject = setValueToField(['one', 'two', 'three'], 'nice');
console.log(targetObject); // Output: { one: { two: { three: 'nice' } } }
Lodash has a _.set method to achieve this
let obj = {}
_.set(obj, ['a', 'b', 'c', 'd'], 'e')
or
_.set(obj, 'a.b.c.d', 'e')
// which generate the following object
{
"a": {
"b": {
"c": {
"d": "e"
}
}
}
}
Here is a simple tweak to jlgrall's answer that allows setting distinct values on each element in the nested hierarchy:
var createNestedObject = function( base, names, values ) {
for( var i in names ) base = base[ names[i] ] = base[ names[i] ] || (values[i] || {});
};
Hope it helps.
Here is a functional solution to dynamically create nested objects.
const nest = (path, obj) => {
const reversedPath = path.split('.').reverse();
const iter = ([head, ...tail], obj) => {
if (!head) {
return obj;
}
const newObj = {[head]: {...obj}};
return iter(tail, newObj);
}
return iter(reversedPath, obj);
}
Example:
const data = {prop: 'someData'};
const path = 'a.deep.path';
const result = nest(path, data);
console.log(JSON.stringify(result));
// {"a":{"deep":{"path":{"prop":"someData"}}}}
Inspired by ImmutableJS setIn method which will never mutate the original. This works with mixed array and object nested values.
function setIn(obj = {}, [prop, ...rest], value) {
const newObj = Array.isArray(obj) ? [...obj] : {...obj};
newObj[prop] = rest.length ? setIn(obj[prop], rest, value) : value;
return newObj;
}
var obj = {
a: {
b: {
c: [
{d: 5}
]
}
}
};
const newObj = setIn(obj, ["a", "b", "c", 0, "x"], "new");
//obj === {a: {b: {c: [{d: 5}]}}}
//newObj === {a: {b: {c: [{d: 5, x: "new"}]}}}
Appreciate that this question is mega old! But after coming across a need to do something like this in node, I made a module and published it to npm.
Nestob
var nestob = require('nestob');
//Create a new nestable object - instead of the standard js object ({})
var newNested = new nestob.Nestable();
//Set nested object properties without having to create the objects first!
newNested.setNested('biscuits.oblong.marmaduke', 'cheese');
newNested.setNested(['orange', 'tartan', 'pipedream'], { poppers: 'astray', numbers: [123,456,789]});
console.log(newNested, newNested.orange.tartan.pipedream);
//{ biscuits: { oblong: { marmaduke: 'cheese' } },
orange: { tartan: { pipedream: [Object] } } } { poppers: 'astray', numbers: [ 123, 456, 789 ] }
//Get nested object properties without having to worry about whether the objects exist
//Pass in a default value to be returned if desired
console.log(newNested.getNested('generic.yoghurt.asguard', 'autodrome'));
//autodrome
//You can also pass in an array containing the object keys
console.log(newNested.getNested(['chosp', 'umbridge', 'dollar'], 'symbols'));
//symbols
//You can also use nestob to modify objects not created using nestob
var normalObj = {};
nestob.setNested(normalObj, 'running.out.of', 'words');
console.log(normalObj);
//{ running: { out: { of: 'words' } } }
console.log(nestob.getNested(normalObj, 'random.things', 'indigo'));
//indigo
console.log(nestob.getNested(normalObj, 'improbable.apricots'));
//false
Inside your loop you can use lodash.set and will create the path for you:
...
const set = require('lodash.set');
const p = {};
const [type, lang, name] = f.split('.');
set(p, [lang, type, name], '');
console.log(p);
// { lang: { 'type': { 'name': '' }}}
try using recursive function:
function createSetting(setting, value, index) {
if (typeof index !== 'number') {
index = 0;
}
if (index+1 == setting.length ) {
settings[setting[index]] = value;
}
else {
settings[setting[index]] = {};
createSetting(setting, value, ++index);
}
}
I think, this is shorter:
Settings = {};
newSettingName = "Modules_Floorplan_Image_Src";
newSettingValue = "JWPlayer";
newSettingNameArray = newSettingName.split("_");
a = Settings;
for (var i = 0 in newSettingNameArray) {
var x = newSettingNameArray[i];
a[x] = i == newSettingNameArray.length-1 ? newSettingValue : {};
a = a[x];
}
I found #jlgrall's answer was great but after simplifying it, it didn't work in Chrome. Here's my fixed should anyone want a lite version:
var callback = 'fn.item1.item2.callbackfunction',
cb = callback.split('.'),
baseObj = window;
function createNestedObject(base, items){
$.each(items, function(i, v){
base = base[v] = (base[v] || {});
});
}
callbackFunction = createNestedObject(baseObj, cb);
console.log(callbackFunction);
I hope this is useful and relevant. Sorry, I've just smashed this example out...
You can define your own Object methods; also I'm using underscore for brevity:
var _ = require('underscore');
// a fast get method for object, by specifying an address with depth
Object.prototype.pick = function(addr) {
if (!_.isArray(addr)) return this[addr]; // if isn't array, just get normally
var tmpo = this;
while (i = addr.shift())
tmpo = tmpo[i];
return tmpo;
};
// a fast set method for object, put value at obj[addr]
Object.prototype.put = function(addr, val) {
if (!_.isArray(addr)) this[addr] = val; // if isn't array, just set normally
this.pick(_.initial(addr))[_.last(addr)] = val;
};
Sample usage:
var obj = {
'foo': {
'bar': 0 }}
obj.pick('foo'); // returns { bar: 0 }
obj.pick(['foo','bar']); // returns 0
obj.put(['foo', 'bar'], -1) // obj becomes {'foo': {'bar': -1}}
A snippet for those who need to create a nested objects with support of array keys to set a value to the end of path. Path is the string like: modal.product.action.review.2.write.survey.data. Based on jlgrall version.
var updateStateQuery = function(state, path, value) {
var names = path.split('.');
for (var i = 0, len = names.length; i < len; i++) {
if (i == (len - 1)) {
state = state[names[i]] = state[names[i]] || value;
}
else if (parseInt(names[i+1]) >= 0) {
state = state[names[i]] = state[names[i]] || [];
}
else {
state = state[names[i]] = state[names[i]] || {};
}
}
};
Set Nested Data:
function setNestedData(root, path, value) {
var paths = path.split('.');
var last_index = paths.length - 1;
paths.forEach(function(key, index) {
if (!(key in root)) root[key] = {};
if (index==last_index) root[key] = value;
root = root[key];
});
return root;
}
var obj = {'existing': 'value'};
setNestedData(obj, 'animal.fish.pet', 'derp');
setNestedData(obj, 'animal.cat.pet', 'musubi');
console.log(JSON.stringify(obj));
// {"existing":"value","animal":{"fish":{"pet":"derp"},"cat":{"pet":"musubi"}}}
Get Nested Data:
function getNestedData(obj, path) {
var index = function(obj, i) { return obj && obj[i]; };
return path.split('.').reduce(index, obj);
}
getNestedData(obj, 'animal.cat.pet')
// "musubi"
getNestedData(obj, 'animal.dog.pet')
// undefined
Try this: https://github.com/silkyland/object-to-formdata
var obj2fd = require('obj2fd/es5').default
var fd = obj2fd({
a:1,
b:[
{c: 3},
{d: 4}
]
})
Result :
fd = [
a => 1,
b => [
c => 3,
d => 4
]
]
Here is a decomposition to several useful functions, that each preserve existing data. Does not handle arrays.
setDeep: Answers question. Non-destructive to other data in the object.
setDefaultDeep: Same, but only sets if not already set.
setDefault: Sets a key if not already set. Same as Python's setdefault.
setStructure: Helper function that builds the path.
// Create a nested structure of objects along path within obj. Only overwrites the final value.
let setDeep = (obj, path, value) =>
setStructure(obj, path.slice(0, -1))[path[path.length - 1]] = value
// Create a nested structure of objects along path within obj. Does not overwrite any value.
let setDefaultDeep = (obj, path, value) =>
setDefault(setStructure(obj, path.slice(0, -1)), path[path.length - 1], value)
// Set obj[key] to value if key is not in object, and return obj[key]
let setDefault = (obj, key, value) =>
obj[key] = key in obj ? obj[key] : value;
// Create a nested structure of objects along path within obj. Does not overwrite any value.
let setStructure = (obj, path) =>
path.reduce((obj, segment) => setDefault(obj, segment, {}), obj);
// EXAMPLES
let temp = {};
// returns the set value, similar to assignment
console.log('temp.a.b.c.d:',
setDeep(temp, ['a', 'b', 'c', 'd'], 'one'))
// not destructive to 'one'
setDeep(temp, ['a', 'b', 'z'], 'two')
// does not overwrite, returns previously set value
console.log('temp.a.b.z: ',
setDefaultDeep(temp, ['a', 'b', 'z'], 'unused'))
// creates new, returns current value
console.log('temp["a.1"]: ',
setDefault(temp, 'a.1', 'three'))
// can also be used as a getter
console.log("temp.x.y.z: ",
setStructure(temp, ['x', 'y', 'z']))
console.log("final object:", temp)
I'm not sure why anyone would want string paths:
They are ambiguous for keys with periods
You have to build the strings in the first place
Since I started with something from this page, I wanted to contribute back
Other examples overwrote the final node even if it was set, and that wasn't what I wanted.
Also, if returnObj is set to true, it returns the base object. By default, falsy, it returns the deepest node.
function param(obj, path, value, returnObj) {
if (typeof path == 'string') path = path.split(".");
var child = obj;
path.forEach((key, i) => {
if (!(key in child)) {
child[key] = (i < path.length-1) ? {} : value || {};
}
child = child[key];
});
return returnObj ? obj : child;
}
var x = {};
var xOut = param(x, "y.z", "setting")
console.log(xOut);
xOut = param(x, "y.z", "overwrite") // won't set
console.log(xOut);
xOut = param(x, "y.a", "setting2")
console.log(xOut);
xOut = param(x, "y.a", "setting2", true) // get object rather than deepest node.
console.log(xOut);
You can also do something where numeric keys are placed in arrays (if they don't already exist). Note that numeric keys won't convert to arrays for the first element of the path, since that's set by the type of your base-object.
function isNumber(n) {
return !isNaN(parseFloat(n)) && isFinite(n);
}
function param(obj, path, value, returnObj) {
if (typeof path == 'string') path = path.split(".");
var child = obj;
path.forEach((key, i) => {
var nextKey = path[i+1];
if (!(key in child)) {
child[key] = (nextKey == undefined && value != undefined
? value
: isNumber(nextKey)
? []
: {});
}
child = child[key];
});
return returnObj ? obj : child;
}
var x = {};
var xOut = param(x, "y.z", "setting")
console.log(xOut);
xOut = param(x, "y.z", "overwrite") // won't set
console.log(xOut);
xOut = param(x, "y.a", "setting2")
console.log(xOut);
xOut = param(x, "y.a", "setting2", true) // get object rather than deepest node.
xOut = param(x, "1.0.2.a", "setting")
xOut = param(x, "1.0.1.a", "try to override") // won't set
xOut = param(x, "1.0.5.a", "new-setting", true) // get object rather than deepest node.
console.log(xOut);
Naturally, when the numeric keys are greater than 0, you might see some undefined gaps.
Practical uses of this might be
function AddNote(book, page, line) {
// assume a global global notes collection
var myNotes = param(allNotes, [book, page, line], []);
myNotes.push('This was a great twist!')
return myNotes;
}
var allNotes = {}
var youthfulHopes = AddNote('A Game of Thrones', 4, 2, "I'm already hooked, at least I won't have to wait long for the books to come out!");
console.log(allNotes)
// {"A Game of Thrones": [undefined, undefined, undefined, undefined, [undefined, undefined, ["I'm already hooked, at least I won't have to wait long for the books to come out!"]]]}
console.log(youthfulHopes)
// ["I'm already hooked, at least I won't have to wait long for the books to come out!"]
function initPath(obj, path) {
path.split('.').reduce((o, key) => (
Object.assign(o, {[key]: Object(o[key])}),
o[key]
), obj);
return obj;
}
Usage
const obj = { a: { b: 'value1' } };
initPath(obj, 'a.c.d').a.c.d='value2';
/*
{
"a": {
"b": "value1",
"c": {
"d": "value2"
}
}
}
*/
simple answer. on es6, im using this
const assign = (obj, path, value) => {
let keyPath = path.split('.')
let lastKeyIndex = keyPath.length - 1
for (let i = 0; i < lastKeyIndex; ++i) {
let key = keyPath[i]
if (!(key in obj)) {
obj[key] = {}
}
obj = obj[key]
}
obj[keyPath[lastKeyIndex]] = value
}
example json
const obj = {
b: 'hello'
}
you can add new key
assign(obj, 'c.d.e', 'this value')
and you get like bellow
console.log(obj)
//response example
obj = {
b: 'hello',
c: {
d: {
e: 'this value'
}
}
}
function createObj(keys, value) {
let obj = {}
let schema = obj
keys = keys.split('.')
for (let i = 0; i < keys.length - 1; i++) {
schema[keys[i]] = {}
schema = schema[keys[i]]
}
schema[keys.pop()] = value
return obj
}
let keys = 'value1.value2.value3'
let value = 'Hello'
let obj = createObj(keys, value)
Eval is probably overkill but the result is simple to visualize, with no nested loops or recursion.
function buildDir(obj, path){
var paths = path.split('_');
var final = paths.pop();
for (let i = 1; i <= paths.length; i++) {
var key = "obj['" + paths.slice(0, i).join("']['") + "']"
console.log(key)
eval(`${key} = {}`)
}
eval(`${key} = '${final}'`)
return obj
}
var newSettingName = "Modules_Video_Plugin_JWPlayer";
var Settings = buildDir( {}, newSettingName );
Basically you are progressively writing a string "obj['one']= {}", "obj['one']['two']"= {} and evaling it;

JavaScript--merge two array objects into one arr object then loop through each index/object

var arrObj1 = [{a:1},{b:2}, {c:3}]
var arrObj2 = [{operator: LESS THAN}, {operator:GREATER THAN}, {operator:"NOT EQUAL"}]
so I want to merge obj at arrObj2[0] into obj at arrObj1[0], arrObj2[1] into obj at arrObj1[1] and so forth...
//resultArrObj = [{a:1, operator: LESS THAN}, {b:2, operator:"GREATER THAN}];
After (or while merging) i need to loop through each object and return a new obj which will have the properties(field, input, operator) and push into array.
field = key from arrObj1 (a, b, c)
input = value from arrObj1(1,2,3)
operator = "operator" from arrObj 2.
// var endResult = [{field:a, input:1, operator: LESS THAN}, {field:b, input:2, operator:GREATER THAN}];
thanks!!
I believe this does what you are looking for. It assumes that you know your data and doesn't do any error checking but just combines them the way that you asked.
var arrObj1 = [{a:1},{b:2}, {c:3}];
var arrObj2 = [{operator: "LESS THAN"}, {operator:"GREATER THAN"}, {operator:"NOT EQUAL"}];
var endResult = [];
var counter = 0;
var objNew = {};
arrObj1.forEach(function(fieldInput){
for ( field in fieldInput ){
objNew.field=field;
objNew.input=fieldInput[field];
objNew.operator=arrObj2[counter]['operator'];
counter++;
endResult.push(objNew);
objNew={};
};
})
// var endResult = [{field:a, input:1, operator: LESS THAN}, {field:b, input:2, operator:GREATER THAN}, {field:c, input:3, operator:NOT EQUAL}]
Let's break this problem into 2 steps:
1) Merging objects
2) Processing objects
We can write a generalized merging function:
if (!Object.prototype.extend) {
Object.prototype.extend = function (object) {
for (key in object) {
if (typeof object[key] === 'object' &&
typeof this[key] === 'object' &&
this.hasOwnProperty(key)) {
this[key].extend(object[key]);
} else {
this[key] = object[key];
}
}
return this;
};
};
The code above creates a method that all Objects share. It works like this:
var obj1 = { 'a': 7 },
obj2 = { 'b': 2 };
obj1.extend(obj2);
console.log(obj1); // { 'a': 7, 'b': 2 };
I usually wouldn't modify Objects like I have done with the .extend method, but it should be trivial to build something that does the same thing but has a different API. (for example, function extend (obj1, obj2) { do stuff.. and return extended object }
Anyways. That should solve the first part about merging objects.
The second part, processing this merged object, is simply using a for loop that iterates over the merged object, and pushes new objects into an array. It would look something like this:
for (var prop in mergedObject) {
var tempObject = {};
if (prop === 'operator') {
tempObject.operator = mergedObject.operator;
} else {
tempObject.field = prop;
tempObject.input = tempObject[prop];
}
endResult.push(tempObject);
}
Hopefully this helps you.

Trying to loop through arrays containing arrays containing objects, to match data

I'm tryign to write code that will loop through an array "productsArray" and match it against my productPropertyArray to pull matching information.
however productsArray is an array in an array that contains an object with the data. My Question is how can I loop through both arrays and then return the matching data.
Current function:
var pList = productsArray
if (productPropertyArray.length === 0 || productsArray.length === 0) return [];
for (var i = 0; i < pList.length; i++) {
for (var j = 0; j < pList[i].length; j++) {
if (pList[i][j] === productPropertyArray) {
return productPropertyArray;
} else {
continue;
}
}
}
return [];
};
example of pList:
productsArray = [
[{"sku" : "131674"},
{"sku" : "84172"}],
[{"productID" : "1234"}
,{"productID" : "12345"}],
[{"test": 1},{"test": 1}],
[{"test": 1},{"sellAlone": false,"test": 1}],
[{"test": 1}],
[{"sellAlone": false,"test": 1}]
];
example of productPropertyArray: (its an argument thats replaced by the following)
productSKUArray = [
"00544MF24F575",
"131674",
"84172"
];
productPropertyArray is just an argument in the function which is replaced by productSKUArray The setup goes like this: function(productProperty, productPropertyArray, productsArray) {
productProperty is just a string that contains sku or productID
any ideas are appreciated. thanks.
Check this out:
http://jsfiddle.net/v9d7bjms/2/
function find() {
var productsArray = [
[{"sku" : "131674"},
{"sku" : "84172"}],
[{"productID" : "1234"}
,{"productID" : "12345"}],
[{"test": 1},{"test": 1}],
[{"test": 1},{"sellAlone": false,"test": 1}],
[{"test": "00544MF24F575"}],
[{"sellAlone": false,"test": 1}]
],
pList = productsArray,
productSKUArray = [
"00544MF24F575",
"131674",
"84172"
];
// All arrays matching your productsSKUArray
var findings = productsArray.filter(function (productProperty) {
// .some returns true after finding first matching element (and breaks the loop)
return productProperty.some(function (obj) {
var keys = Object.keys(obj);
// We need to get all the "values" from object so we interate over
// the keys and check if any value matches something from productSKUArray
return keys.some(function (key) {
// Check if value exists in productsSKUArray
return productSKUArray.indexOf(obj[key]) > -1;
});
});
});
return findings;
}
console.log(find());
.filter will return all arrays containing objects with values from productSKUArray.
See Array.prototype.filter, Array.prototype.some and Array.prototype.indexOf for method reference.
The inner if needs to refer to pList[i][j].
This will output [{sku: "131674"}, {sku: "84172"}].
var matchingData = [];
for(var productProperties in productsArray){
var pp = productsArray[productProperties];
for(var property in pp) {
var p = pp[property];
for(var propertyName in p){
var propertyValue = p[propertyName];
for(var i in productSKUArray){
if(propertyValue == productSKUArray[i]){
matchingData.push(p);
break;
}
}
}
}
}
but this is just the brute force solution.

How to sum the values of a JavaScript object?

I'd like to sum the values of an object.
I'm used to python where it would just be:
sample = { 'a': 1 , 'b': 2 , 'c':3 };
summed = sum(sample.itervalues())
The following code works, but it's a lot of code:
function obj_values(object) {
var results = [];
for (var property in object)
results.push(object[property]);
return results;
}
function list_sum( list ){
return list.reduce(function(previousValue, currentValue, index, array){
return previousValue + currentValue;
});
}
function object_values_sum( obj ){
return list_sum(obj_values(obj));
}
var sample = { a: 1 , b: 2 , c:3 };
var summed = list_sum(obj_values(a));
var summed = object_values_sum(a)
Am i missing anything obvious, or is this just the way it is?
It can be as simple as that:
const sumValues = obj => Object.values(obj).reduce((a, b) => a + b, 0);
Quoting MDN:
The Object.values() method returns an array of a given object's own enumerable property values, in the same order as that provided by a for...in loop (the difference being that a for-in loop enumerates properties in the prototype chain as well).
from Object.values() on MDN
The reduce() method applies a function against an accumulator and each value of the array (from left-to-right) to reduce it to a single value.
from Array.prototype.reduce() on MDN
You can use this function like that:
sumValues({a: 4, b: 6, c: -5, d: 0}); // gives 5
Note that this code uses some ECMAScript features which are not supported by some older browsers (like IE). You might need to use Babel to compile your code.
You could put it all in one function:
function sum( obj ) {
var sum = 0;
for( var el in obj ) {
if( obj.hasOwnProperty( el ) ) {
sum += parseFloat( obj[el] );
}
}
return sum;
}
var sample = { a: 1 , b: 2 , c:3 };
var summed = sum( sample );
console.log( "sum: "+summed );
For fun's sake here is another implementation using Object.keys() and Array.reduce() (browser support should not be a big issue anymore):
function sum(obj) {
return Object.keys(obj).reduce((sum,key)=>sum+parseFloat(obj[key]||0),0);
}
let sample = { a: 1 , b: 2 , c:3 };
console.log(`sum:${sum(sample)}`);
But this seems to be way slower: jsperf.com
If you're using lodash you can do something like
_.sum(_.values({ 'a': 1 , 'b': 2 , 'c':3 }))
Now you can make use of reduce function and get the sum.
const object1 = { 'a': 1 , 'b': 2 , 'c':3 }
console.log(Object.values(object1).reduce((a, b) => a + b, 0));
A regular for loop is pretty concise:
var total = 0;
for (var property in object) {
total += object[property];
}
You might have to add in object.hasOwnProperty if you modified the prototype.
Honestly, given our "modern times" I'd go with a functional programming approach whenever possible, like so:
const sumValues = (obj) => Object.keys(obj).reduce((acc, value) => acc + obj[value], 0);
Our accumulator acc, starting with a value of 0, is accumulating all looped values of our object. This has the added benefit of not depending on any internal or external variables; it's a constant function so it won't be accidentally overwritten... win for ES2015!
Any reason you're not just using a simple for...in loop?
var sample = { a: 1 , b: 2 , c:3 };
var summed = 0;
for (var key in sample) {
summed += sample[key];
};
http://jsfiddle.net/vZhXs/
let prices = {
"apple": 100,
"banana": 300,
"orange": 250
};
let sum = 0;
for (let price of Object.values(prices)) {
sum += price;
}
alert(sum)
I am a bit tardy to the party, however, if you require a more robust and flexible solution then here is my contribution. If you want to sum only a specific property in a nested object/array combo, as well as perform other aggregate methods, then here is a little function I have been using on a React project:
var aggregateProperty = function(obj, property, aggregate, shallow, depth) {
//return aggregated value of a specific property within an object (or array of objects..)
if ((typeof obj !== 'object' && typeof obj !== 'array') || !property) {
return;
}
obj = JSON.parse(JSON.stringify(obj)); //an ugly way of copying the data object instead of pointing to its reference (so the original data remains unaffected)
const validAggregates = [ 'sum', 'min', 'max', 'count' ];
aggregate = (validAggregates.indexOf(aggregate.toLowerCase()) !== -1 ? aggregate.toLowerCase() : 'sum'); //default to sum
//default to false (if true, only searches (n) levels deep ignoring deeply nested data)
if (shallow === true) {
shallow = 2;
} else if (isNaN(shallow) || shallow < 2) {
shallow = false;
}
if (isNaN(depth)) {
depth = 1; //how far down the rabbit hole have we travelled?
}
var value = ((aggregate == 'min' || aggregate == 'max') ? null : 0);
for (var prop in obj) {
if (!obj.hasOwnProperty(prop)) {
continue;
}
var propValue = obj[prop];
var nested = (typeof propValue === 'object' || typeof propValue === 'array');
if (nested) {
//the property is an object or an array
if (prop == property && aggregate == 'count') {
value++;
}
if (shallow === false || depth < shallow) {
propValue = aggregateProperty(propValue, property, aggregate, shallow, depth+1); //recursively aggregate nested objects and arrays
} else {
continue; //skip this property
}
}
//aggregate the properties value based on the selected aggregation method
if ((prop == property || nested) && propValue) {
switch(aggregate) {
case 'sum':
if (!isNaN(propValue)) {
value += propValue;
}
break;
case 'min':
if ((propValue < value) || !value) {
value = propValue;
}
break;
case 'max':
if ((propValue > value) || !value) {
value = propValue;
}
break;
case 'count':
if (propValue) {
if (nested) {
value += propValue;
} else {
value++;
}
}
break;
}
}
}
return value;
}
It is recursive, non ES6, and it should work in most semi-modern browsers. You use it like this:
const onlineCount = aggregateProperty(this.props.contacts, 'online', 'count');
Parameter breakdown:
obj = either an object or an array
property = the property within the nested objects/arrays you wish to perform the aggregate method on
aggregate = the aggregate method (sum, min, max, or count)
shallow = can either be set to true/false or a numeric value
depth = should be left null or undefined (it is used to track the subsequent recursive callbacks)
Shallow can be used to enhance performance if you know that you will not need to search deeply nested data. For instance if you had the following array:
[
{
id: 1,
otherData: { ... },
valueToBeTotaled: ?
},
{
id: 2,
otherData: { ... },
valueToBeTotaled: ?
},
{
id: 3,
otherData: { ... },
valueToBeTotaled: ?
},
...
]
If you wanted to avoid looping through the otherData property since the value you are going to be aggregating is not nested that deeply, you could set shallow to true.
Use Lodash
import _ from 'Lodash';
var object_array = [{a: 1, b: 2, c: 3}, {a: 4, b: 5, c: 6}];
return _.sumBy(object_array, 'c')
// return => 9
I came across this solution from #jbabey while trying to solve a similar problem. With a little modification, I got it right. In my case, the object keys are numbers (489) and strings ("489"). Hence to solve this, each key is parse. The following code works:
var array = {"nR": 22, "nH": 7, "totB": "2761", "nSR": 16, "htRb": "91981"}
var parskey = 0;
for (var key in array) {
parskey = parseInt(array[key]);
sum += parskey;
};
return(sum);
A ramda one liner:
import {
compose,
sum,
values,
} from 'ramda'
export const sumValues = compose(sum, values);
Use:
const summed = sumValues({ 'a': 1 , 'b': 2 , 'c':3 });
We can iterate object using in keyword and can perform any arithmetic operation.
// input
const sample = {
'a': 1,
'b': 2,
'c': 3
};
// var
let sum = 0;
// object iteration
for (key in sample) {
//sum
sum += (+sample[key]);
}
// result
console.log("sum:=>", sum);
A simple solution would be to use the for..in loop to find the sum.
function findSum(obj){
let sum = 0;
for(property in obj){
sum += obj[property];
}
return sum;
}
var sample = { a: 1 , b: 2 , c:3 };
console.log(findSum(sample));
function myFunction(a) { return Object.values(a).reduce((sum, cur) => sum + cur, 0); }
Sum the object key value by parse Integer. Converting string format to integer and summing the values
var obj = {
pay: 22
};
obj.pay;
console.log(obj.pay);
var x = parseInt(obj.pay);
console.log(x + 20);
function totalAmountAdjectives(obj) {
let sum = 0;
for(let el in obj) {
sum += el.length;
}
return sum;
}
console.log(totalAmountAdjectives({ a: "apple" }))
A simple and clean solution for typescrip:
const sample = { a: 1, b: 2, c: 3 };
const totalSample = Object.values(sample).reduce(
(total: number, currentElement: number) => total + currentElement
);
console.log(totalSample);
Good luck!

Combining JSON Arrays

I have 3 json arrays, each with information listed in the same format:
Array:
ID:
NAME:
DATA:
ID:
NAME:
DATA:
etc...
My goal is to combine all 3 arrays into one array, and sort and display by NAME by passing the 3 arrays into a function.
The function I've tried is:
Javascript Call:
// to save time I'm just passing the name of the array, I've tried passing
// the full array name as json[0]['DATA'][array_1][0]['NAME'] as well.
combineNames(['array_1','array_2']);
FUNCTION:
function combineNames(names) {
var allNames = []
for (i=0;i<names.length;i++) {
for (j=0;j<json[0]['DATA'][names[i]].length;j++) {
allNames.push(json[0]['DATA'][names[i]][j]['NAME']);
}
}
return allNames.sort();
}
The above gives me the error that NAME is null or undefined.
I've also tried using the array.concat function which works when I hard code it:
var names = [];
var allNames = [];
var names = names.concat(json[0]['DATA']['array_1'],json[0]['DATA']['array_2']);
for (i=0;i<names.length;i++) {
allNames.push(names[i]['NAME']);
}
return allNames.sort();
But I can't figure out how to pass in the arrays into the function (and if possible I would like to just pass in the array name part instead of the whole json[0]['DATA']['array_name'] like I was trying to do in the first function...
you can combine JSON easily with jQuery :
var x ={ a:1, b:2 };
var y ={ a:2, c:3 };
var z ={ b:3, d:4 };
$.extend(x, y, z);
console.dir(x); // now 'x' is all of them combined
If you've got 3 arrays like this:
[{ "id":1, "name":"Bob", "data":1},{ "id":2, "name":"Fred", "data":2 }]
Simply do:
function combine() {
var ar = [];
return ar.concat.apply(ar, arguments).sort(function (a, b) {
var aName = a.NAME;
var bName = b.NAME;
if (aName < bName) {
return -1;
} else if (aName == bName) {
return 0;
} else {
return 1;
};
});
};
Then call it like:
var jointArrays = combine(array1, array2, array3, ...);
However, if your JSON looks like this:
json[0]['DATA'][array_1]
json[0]['DATA'][array_2]
json[0]['DATA'][array_3]
You can simply define combine() as follows, which will be more convenient:
function combine(arrays) {
var ar = [];
return ar.concat.apply(ar, arrays).sort(function (a, b) {
var aName = a.NAME;
var bName = b.NAME;
if (aName < bName) {
return -1;
} else if (aName == bName) {
return 0;
} else {
return 1;
};
});
};
Then call it like:
var jointArrays = combine(json[0].DATA);
If you're wanting an array of just the names, rather than the objects, use the following:
function combine(arrays) {
var ar = [],
ret = [];
ar = ar.concat.apply(ar, arrays);
for (var i=0;i<ar.length;i++) {
ret.push(ar.NAME);
};
return ret.sort();
};
Javascript is case sensitive; make sure it's DATA and not data, and NAME and not name.
Now for a little bit of housekeeping.
In your example, both of your counter variables are being declared as "implied globals", because you're not prefixing them with the var statement (and implied globals are bad). You should use:
for (var i=0;i<something.length;i++) {
//
};
Instead of neglecting the var.
Also, "{}" creates an object. "[]" creates an array. Javascript does not support associative array's; e.g array's with keys that are anything except a number. What you're JSON is returning is an array of objects
"Square notation" and "dot notation" are interchangeable. object["one"] is equivalent to object.one
Square notation is generally used when the key is stored as a variable, or when you're accessing an array.
var key = "one";
object[key]
Hope this helps.
You're redeclaring the allNames variable, emptying it.
Try this:
function combineNames(names) {
var allNames = [];
var data = json[0]['DATA'];
for (arrnames in data) {
for (j=0;j<data[arrnames].length;j++) {
if ('NAME' in data[arrnames]){
allNames.push(data[arrnames]['NAME']);
}
}
}
return allNames.sort();
}
function allNames(names) {
var allNames = [];
for (var i=0;i<names.length;i++) {
for (var j=0;j<json[0]['DATA'][names[i]].length;j++) {
allNames.push(json[0]['DATA'][names[i]][j]['NAME']);
}
}
return allNames.sort();
}
called using:
allNames(['array_1','array_2']);
Seems to work.

Categories