Related
update
I have adjusted/corrected the example objects, because they contained an error before.
I have an mapping object that looks like this:
var AtoB = {
"amore" : "love",
"alimenti": "food",
"Bier" : "beer"
};
which allows to map one way i.e. AtoB["love"] yields "amore". I could add an inverse to it manualy i.e.
var BtoA = {
"love": "amore",
"food": "alimenti",
"beer": "Bier"
};
Anyway it troublesome the two objects in sync and I would like to create the BtoA programmatically in Javascript. Is there some sort of function xyz() which yields var BtoA = xyz(AtoB);?
The example above can be extended to include a problem (e.g. if I have too much "beer")
var AtoB = {
"amore" : "love",
"alimenti": "food",
"Bier" : "beer"
"cerveza" : "beer",
"pivo" : "beer",
"birra" : "beer",
"cerveja" : "beer"
};
as this is not a 1-to-1 mapping. In amateuer math terms It is not an inversible function?
To make things even more complicated I have a recipe for desaster.
var makeStuff = {
"agriculture": "food",
"hops" : {
"water": {
"malt": "beer"},
"malt": {
"water": "beer"}},
"water" : {
"hops": {
"malt": "beer"},
"malt": {
"hops": "beer"}},
"malt" : {
"water": {
"hops": "beer"},
"hops": {
"water": "beer"}}
};
inversing this nested javascript object, seems even more challanging for such an xyz() function. Anyway maybe there is such an xyz() function, then I would be glad to accept this as an answer to this question
Very simple. Following is the code to inverse key, value.
var inverse= (function inv(param){
for(var attr in param) {
if(param.hasOwnProperty(attr)) {
if(typeof param[attr]==="string") {
param[param[attr]] = attr;
delete param[attr];
} else if (Object.prototype.toString.call(param[attr]) === "[object Object]") {
param[attr] = inv(param[attr]);
}
}
}
return param;
});
To get the result into other object, initialize it with empty and assign it. Like
var BtoA = {};
BtoA = inverse(AtoB);
And, The JSON:
var AtoB = {
"love": "amore",
"food": "alimenti",
"beer": "Bier",
"beer": "cerveza",
"beer": "pivo",
"beer": "birra",
"beer": "cerveja",
};
has only three attributes because JSON is a dictionary data structure: new key will replace the old one. So the above JSON will actually be like:
{love: "amore", food: "alimenti", beer: "cerveja"}
So, inverting the above given JSON (AtoB) will result in the inversion of only three properties, and final result will be:
{amore: "love", alimenti: "food", cerveja: "beer"}
The answer from Muhammad imran is effective if the purpose/target is a simple inversion (i.e. no nested object structure; no multiple values).
Obviously that is the best result to be achieved, if no further artifice is created, to cover the fact that the key->value relation in objects are:
keys are unique,
values can be muliple.
Looking at the beer example above it is somewhat regretible that the information is lost in the inversion. Therefore this answer should supplement and enrich and provide a way in which the information can be stored. The way to achieve it is using Javascript Arrays within the resulting inverted object, to allow to store the potentially ambigious new values. as for example.
var BeerAtoB = {
"amore" : "love",
"alimenti": "food",
"Bier" : "beer",
"cerveza" : "beer",
"pivo" : "beer",
"birra" : "beer",
"cerveja" : "beer"
};
allowing to translate (de,es,pl/cz,it,pt)"beer" to English would best store
this information in the inverted too
var BeerBtoA = {
"love" : "amore",
"food" : "alimenti",
"beer" : [ "Bier" ,
"cerveza",
"pivo",
"birra",
"cerveja"
]
};
a version in which less information get lost and the multipleness of the original value "beer" is reflected by multipleness of values under the joint, inverted key "beer" now.
To accomplish this I made an enhanced inverting function
function invertObject(obj)
{
var invertedObject = {};
// make a stack and prime it with the obj
var stack = [];
stack.push({"way":[],"obj":obj});
// while stuff on the stack
while (stack.length)
{
var way= stack[0].way;
var obj= stack[0].obj;
for (var prop in obj)
{
if (typeof obj[prop] === 'object')
{
// attributes, which are themselves objects are added to the stack,
// with their way information.
stack.push({"way":way.concat(prop),"obj":obj[prop]});
}
else
{
// always start with adding things to the invertedObject,
var curobj = invertedObject;
var value = newKey = obj[prop];
var curpath = way.concat(prop).concat(obj[prop]);
// for all but the last two path elements the loop below
// will create the inverted path, starting with the value (obj[prop])
// as key, Since values need not be unique (as keys), create each
// such new key-property as an Array, not to loose inverted pathes.
while(curpath.length>2)
{
var pathpart = curpath.pop();
if(!curobj.hasOwnProperty(pathpart))
{
curobj[pathpart]=[];
}
curobj=curobj[pathpart];
}
// the last two curpath Array members represent the last key and the
// new to be added value.
var preLastPart = curpath.pop();
var lastPart = curpath.pop();
// Again the artifice of an Array is used since
// the inverted keys are not unique, hence cases in which
// 1 key has (>1) values.
if(!curobj.hasOwnProperty(preLastPart))
{
curobj[preLastPart]=[];
}
curobj[preLastPart].push(lastPart);
}
}
stack.shift();
}
return invertedObject; function invertObject(obj)
{
var invertedObject = {};
// make a stack and prime it with the obj
var stack = [];
stack.push({"way":[],"obj":obj});
// while stuff on the stack
while (stack.length)
{
var way= stack[0].way;
var obj= stack[0].obj;
for (var prop in obj)
{
if (typeof obj[prop] === 'object')
{
// attributes, which are themselves objects are added to the stack,
// with their way information.
stack.push({"way":way.concat(prop),"obj":obj[prop]});
}
else
{
// always start with adding things to the invertedObject,
var curobj = invertedObject;
var value = newKey = obj[prop];
var curpath = way.concat(prop).concat(obj[prop]);
// for all but the last two path elements the loop below
// will create the inverted path, starting with the value (obj[prop])
// as key, Since values need not be unique (as keys), create each
// such new key-property as an Array, not to loose inverted pathes.
while(curpath.length>2)
{
var pathpart = curpath.pop();
if(!curobj.hasOwnProperty(pathpart))
{
curobj[pathpart]=[];
}
curobj=curobj[pathpart];
}
// the last two curpath Array members represent the last key and the
// new to be added value.
var preLastPart = curpath.pop();
var lastPart = curpath.pop();
// Again the artifice of an Array is used since
// the inverted keys are not unique, hence cases in which
// 1 key has (>1) values.
if(!curobj.hasOwnProperty(preLastPart))
{
curobj[preLastPart]=[];
}
curobj[preLastPart].push(lastPart);
}
}
stack.shift();
}
return invertedObject;
}
}
Indeed since equal values can be found in many places of simple and nested object, the result will be an object in which each value will be an Array for two reasons:
Several original object's keys, can have the same value, and therefore (after inverting) more than one value can exists. An Array can store all those multiple new values, hence all information.
While in an nested object, the uniqueness makes every property either a direct value or a subobject, in the inverted object at a key, we can find not only muliple values, but also that at the very same place there are also further nested objects. (For this reason it is lucky that an Javascript Array, as being an Object, does besides its entry allow also for further properties to be attached to it and hence can serve simultaneously as a storage for the multiple values and as a subkey in the nested structure. Such a double purpose of Arrays in the inverted object structure, is unforunatelly hard to show in JSON notation, as the JSON notation does not allow for Arrays with Object Attributes)
I have this object:
key = {
spawn:{type:1,img:app.assets.get('assets/spawn.svg')},
wall:{type:2,img:app.assets.get('assets/wall.svg')},
grass:{type:3,img:app.assets.get('assets/grass.svg')},
spike:{type:4,img:app.assets.get('assets/spike.svg')},
ground:{type:5,img:app.assets.get('assets/ground.svg')}
};
And I have an array with only types and I need to add the given image to it, the array looks something like this:
[{type:1,image:null},{type:3,image:null},{type:2,image:null},{type:2,image:null},{type:5,image:null}]
Basically I want to loop the array, find the type in the key object and get the given image and save it into the array.
Is there any simple way to do this?
One thing that stands out here for me is the line
...get the given image and save it into the array
I'm assuming this means the original array. I think a better approach would be to map the appropriate keys and values to a new array but I've assumed, for this example, that it's a requirement.
In an attempt to keep the solution as terse as possible and the request for a lodash solution:
_.each(key, function(prop){
_.each(_.filter(types, { type: prop.type }), function(type) { type.image = prop.img });
});
Given the object of keys and an array of objects like so:
var key = {
spawn:{type:1,img:app.assets.get('assets/spawn.svg')},
wall:{type:2,img:app.assets.get('assets/wall.svg')},
grass:{type:3,img:app.assets.get('assets/grass.svg')},
spike:{type:4,img:app.assets.get('assets/spike.svg')},
ground:{type:5,img:app.assets.get('assets/ground.svg')}
};
var arr = [{type:1,image:null},{type:3,image:null},{type:2,image:null},{type:2,image:null},{type:5,image:null}];
We can first create an array of the properties in the object key to make iterating it simpler.
Then loop over the array arr, and upon each member, check with a some loop which image belongs to the member by its type (some returning on the first true and ending the loop).
You can change the forEach to a map (and assign the returned new array to arr or a new variable) if you want the loop to be without side-effects, and not to mutate the original array.
var keyTypes = Object.keys(key);
arr.forEach(function (item) {
keyTypes.some(function (keyType) {
if (key[keyType].type === item.type) {
item.image = key[keyType].img;
return true;
}
return false;
});
});
The smarter thing would be to change the object of the imagetypes so that you could use the type as the accessing property, or create another object for that (as pointed out in another answer).
I'm not sure if this solution is modern, but it does not use any loops or recursion.
object = {
spawn: {type:1, img:app.assets.get('assets/spawn.svg')},
wall: {type:2, img:app.assets.get('assets/wall.svg')},
grass: {type:3, img:app.assets.get('assets/grass.svg')},
spike: {type:4, img:app.assets.get('assets/spike.svg')},
ground: {type:5, img:app.assets.get('assets/ground.svg')}
};
arr = [
{type:1, image:null},
{type:3, image:null},
{type:2, image:null},
{type:2, image:null},
{type:5, image:null}
];
var typeImages = {};
Object.getOwnPropertyNames(object).forEach(function(value){
typeImages[object[value].type] = object[value].img;
});
arr = arr.map(function(value){
return {
type: value.type,
image: typeImages[value.type]
};
});
var key = {
spawn:{type:1,img:app.assets.get('assets/spawn.svg')},
wall:{type:2,img:app.assets.get('assets/wall.svg')},
grass:{type:3,img:app.assets.get('assets/grass.svg')},
spike:{type:4,img:app.assets.get('assets/spike.svg')},
ground:{type:5,img:app.assets.get('assets/ground.svg')}
};
var typesArray = [{type:1,image:null},{type:3,image:null},{type:2,image:null},{type:2,image:null},{type:5,image:null}];
for(var i = 0, j = typesArray.length; i < j; i++)
{
typesArray[i].image = getKeyObjectFromType(typesArray[i].type).img;
}
function getKeyObjectFromType(type)
{
for(var k in key)
{
if(key[k].type == type)
{
return key[k];
}
}
return {};
}
for (var i = 0; i < typesArray.length; i++) {
for (prop in key) {
if (key[prop].type === typesArray[i].type) {
typesArray[i].image = key[prop].img;
}
}
}
It loops through the array ("typesArray"), and for each array item, it go through all the objects in key looking for the one with the same "type". When it finds it, it takes that key object's "img" and saves into the array.
Using lodash (https://lodash.com/):
var key = {
spawn:{type:1,img:app.assets.get('assets/spawn.svg')},
wall:{type:2,img:app.assets.get('assets/wall.svg')},
grass:{type:3,img:app.assets.get('assets/grass.svg')},
spike:{type:4,img:app.assets.get('assets/spike.svg')},
ground:{type:5,img:app.assets.get('assets/ground.svg')}
};
var initialList = [{type:1,image:null},{type:3,image:null},{type:2,image:null},{type:2,image:null},{type:5,image:null}];
var updatedList = _.transform(initialList, function(result, item) {
item.image = _.find(key, _.matchesProperty('type', item.type)).img;
result.push(item);
});
This will go over every item in the initialList, find the object that matched their type property in key and put it in the image property.
The end result will be in updatedList
I have an array done with values from some radio buttons, say myArray = ["1","40","35"];
every value has his counterparts, say for instance 1 = "men", 2 = "women", 40 = "red hairs".
what's the best method to build another array where every values gets his counterpart?
so something like that myBrandNewArray = ["men","red hairs", …];
I should store my couples into variables for some maintenance, like
var "1" = "men", "2" = "women", … ;
but I don't know if this is a good approach…
ps. even pointing me to some resources will be great. Thank you.
I would keep a Hash of values
hash = { '1': 'Men', '2': 'Women' ... }
Then [ '1', '2', ... ].map( function(v) { return hash[v]; } );
IE9- will not accpet this, in this case you could just iterate in a for loop
Why don't you use an object as associative array?
var array = new Object();
array["1"] = "men"
array["40"] = "red hairs"
You can create an object like:
var arr = {
'1' : 'men',
'2' : 'women'
}
You can always access this easily like : arr['1'] == 'men'
if you want to create from existing arrays:
say myArray & myBrandNewArray
you can do something like
var arr = {};
foreach ( var i in myArray ) {
arr[myArray[i]] = myBrandNewArray[i];
}
i think this function
myArray = ["1","40","35"];
myBrandNewArray = myArray.map(function(element){ /* your code to get the right array 8/ })
source: http://www.tutorialspoint.com/javascript/array_map.htm
there is also a jQuery (cross browser) version of this function, for more details about that look here jQuery.map(myArray, function(value, index){ /*....*/ })
Use an object:
var val = {
1: "men",
40: "red hairs"
};
alert(val[1]);
alert(val[2])
;
well, finally I did this:
having
var numericalArray = ["1","50","45", …];
and
var couples = { "50" : "homme",
"1" : "femme",
"85" : "court",
…
};
I can call this and get a new array with coupled values:
function assignValues(numericalArray) {
var verbalArray = [];
for (var i=0; i<numericalArray.length; i++) {
var value = numericalArray[i];
verbalArray.push(couples[value]); // right, I can't check if the values exists
}
console.log('here my new array:', verbalArray);
}
thanks to have me pointed on use of an object.
I would like to get the keys of a JavaScript object as an array, either in jQuery or pure JavaScript.
Is there a less verbose way than this?
var foo = { 'alpha' : 'puffin', 'beta' : 'beagle' };
var keys = [];
for (var key in foo) {
keys.push(key);
}
Use Object.keys:
var foo = {
'alpha': 'puffin',
'beta': 'beagle'
};
var keys = Object.keys(foo);
console.log(keys) // ['alpha', 'beta']
// (or maybe some other order, keys are unordered).
This is an ES5 feature. This means it works in all modern browsers but will not work in legacy browsers.
The ES5-shim has a implementation of Object.keys you can steal
You can use jQuery's $.map.
var foo = { 'alpha' : 'puffin', 'beta' : 'beagle' },
keys = $.map(foo, function(v, i){
return i;
});
Of course, Object.keys() is the best way to get an Object's keys. If it's not available in your environment, it can be trivially shimmed using code such as in your example (except you'd need to take into account your loop will iterate over all properties up the prototype chain, unlike Object.keys()'s behaviour).
However, your example code...
var foo = { 'alpha' : 'puffin', 'beta' : 'beagle' };
var keys = [];
for (var key in foo) {
keys.push(key);
}
jsFiddle.
...could be modified. You can do the assignment right in the variable part.
var foo = { 'alpha' : 'puffin', 'beta' : 'beagle' };
var keys = [], i = 0;
for (keys[i++] in foo) {}
jsFiddle.
Of course, this behaviour is different to what Object.keys() actually does (jsFiddle). You could simply use the shim on the MDN documentation.
In case you're here looking for something to list the keys of an n-depth nested object as a flat array:
const getObjectKeys = (obj, prefix = '') => {
return Object.entries(obj).reduce((collector, [key, val]) => {
const newKeys = [ ...collector, prefix ? `${prefix}.${key}` : key ]
if (Object.prototype.toString.call(val) === '[object Object]') {
const newPrefix = prefix ? `${prefix}.${key}` : key
const otherKeys = getObjectKeys(val, newPrefix)
return [ ...newKeys, ...otherKeys ]
}
return newKeys
}, [])
}
console.log(getObjectKeys({a: 1, b: 2, c: { d: 3, e: { f: 4 }}}))
I don't know about less verbose but I was inspired to coerce the following onto one line by the one-liner request, don't know how Pythonic it is though ;)
var keys = (function(o){var ks=[]; for(var k in o) ks.push(k); return ks})(foo);
Summary
For getting all of the keys of an Object you can use Object.keys(). Object.keys() takes an object as an argument and returns an array of all the keys.
Example:
const object = {
a: 'string1',
b: 42,
c: 34
};
const keys = Object.keys(object)
console.log(keys);
console.log(keys.length) // we can easily access the total amount of properties the object has
In the above example we store an array of keys in the keys const. We then can easily access the amount of properties on the object by checking the length of the keys array.
Getting the values with: Object.values()
The complementary function of Object.keys() is Object.values(). This function takes an object as an argument and returns an array of values. For example:
const object = {
a: 'random',
b: 22,
c: true
};
console.log(Object.values(object));
Year 2022 and JavaScript still does not have a sound way to work with hashes?
This issues a warning but works:
Object.prototype.keys = function() { return Object.keys(this) }
console.log("Keys of an object: ", { a:1, b:2 }.keys() )
// Keys of an object: Array [ "a", "b" ]
// WARN: Line 8:1: Object prototype is read only, properties should not be added no-extend-native
That said, Extending Built-in Objects is Controversial.
If you decide to use Underscore.js you better do
var foo = { 'alpha' : 'puffin', 'beta' : 'beagle' };
var keys = [];
_.each( foo, function( val, key ) {
keys.push(key);
});
console.log(keys);
I'd like to have JavaScript objects within another JavaScript object as such:
Issues:
- {"ID" : "1", "Name" : "Missing Documentation", "Notes" : "Issue1 Notes"}
- {"ID" : "2", "Name" : "Software Bug", "Notes" : "Issue2 Notes, blah, blah"}
- {"ID" : "2", "Name" : "System Not Ready", "Notes" : "Issue3 Notes, etc"}
// etc...
So, I'd like "Issues" to hold each of these JavaScript objects, so that I can just say Issues[0].Name, or Issues[2].ID, etc.
I've created the outer Issues JavaScript object:
var jsonIssues = {};
I'm to the point where I need to add a JavaScript object to it, but don't know how. I'd like to be able to say:
Issues<code here>.Name = "Missing Documentation";
Issues<code here>.ID = "1";
Issues<code here>.Notes = "Notes, notes notes";
Is there any way to do this? Thanks.
UPDATE: Per answers given, declared an array, and am pushing JavaScript objects on as needed:
var jsonArray_Issues = new Array();
jsonArray_Issues.push( { "ID" : id, "Name" : name, "Notes" : notes } );
Thanks for the responses.
var jsonIssues = []; // new Array
jsonIssues.push( { ID:1, "Name":"whatever" } );
// "push" some more here
As my first object is a native JavaScript object (used like a list of objects), push didn't work in my scenario, but I resolved it by adding new key as follows:
MyObjList['newKey'] = obj;
In addition to this, may be useful to know how to delete the same object as inserted before:
delete MyObjList['newKey'][id];
Hope it helps someone as it helped me.
var jsonIssues = [
{ID:'1',Name:'Some name',Notes:'NOTES'},
{ID:'2',Name:'Some name 2',Notes:'NOTES 2'}
];
If you want to add to the array then you can do this
jsonIssues[jsonIssues.length] = {ID:'3',Name:'Some name 3',Notes:'NOTES 3'};
Or you can use the push technique that the other guy posted, which is also good.
// Merge object2 into object1, recursively
$.extend( true, object1, object2 );
// Merge object2 into object1
$.extend( object1, object2 );
https://api.jquery.com/jquery.extend/
If it's not an array of object you can do this:
let student= {
name : 'Mr. Anderson',
id: 35
}
student['grade'] = 10; //for a property.
Result:
student= {
name : 'Mr. Anderson',
id: 35,
grade:10
}
You also can add an object:
let student= {
personalData:{
//personal data key-value
}
}
let academicData = {
//academic data key-value
}
student['academicData'] = academicData;
Result:
student{
personalData{},
academicData{}
}
jsonIssues = [...jsonIssues,{ID:'3',Name:'name 3',Notes:'NOTES 3'}]
If you have properties in first obj and you have to add your objs to it, then Object.assign() will erase it.
To avoid this loss I've written a function below. Be aware, it copies nested objs by refference.
First you should add all objs you want to add to your's obj to an arr. You can do it easily by arr.push(). Then just use the Fn.
function addMyObjs (objInit, arrWithObjs) {
let map = new Map();
for (let [key, value] of Object.entries(objInit)) {
map.set(key, value);
}
arrWithObjs.forEach((item) => {
for (let [key, value] of Object.entries(item)) {
map.set(key, value);
}
});
return Object.fromEntries(map);
}
let objSecond = {id: 2,};
let obj = {
name: "Tolya",
age: 33,
sex: "man",
};
let obj3 = {"fruits": {"apples": true, "plums": false,}};
let arr = [obj, obj3];
objSecond = addMyObjs(objSecond, arr);