Transforming array into a object in JavaScript - javascript

I am trying to convert an array into a javscript object that is designed to work with input checkboxes in AngularJS.
This is the input array that I get from my backend:
let selectedRolesDB = ['ADMIN', 'SECURITY'];
This is what my front-end expects:
let selectedRoles =
{
'ADMIN' : true,
'SECURITY': true
};
I tried different approaches such as angular.forEach but the problem is that I am not able to get the desired output:
angular.forEach(selectedRolesDB,(value, key) => {
this.selectedRoles.push({value : true });
});
Can anyone tell me how I best solve my problem so I end up with the array that my front-end expects?
JSFiddle

selectedRoles is not array, it is object. Init it as empty object:
let selectedRoles = {};
angular.forEach(selectedRolesDB,(value, key) => {
// use [] notation to add property with required name and value
selectedRoles[value] = true;
});

Use array.reduce :
let selectedRolesDB = ['ADMIN', 'SECURITY'];
const newArray = selectedRolesDB.reduce((accumulator, value) => {
accumulator[value] = true;
return accumulator;
}, {});
console.log(newArray)
See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce for documentation about it.

Probably it would be much better to use array's native 'forEach' method (it has a good browser support, see here Array forEach). Also this will be helpful if you decide to migrate your project into Angular2+, so avoiding usages of 'angular.someMethod' is a better approach.
This is a final solution:
const selectedRoles: {[key: string]: boolean} = {};
selectedRolesDB.forEach((value: string) => selectedRoles[value] = true);

Related

ES6 / Lodash, how to check if an object contains a specific value of given request?

With having an object like below, how would I know that a certain value, e.g: template:d51a08fe-fb60-4da0-841b-03a732f19576, existed in this object?
const obj= {
"5871": "template:d51a08fe-fb60-4da0-841b-03a732f19576",
"6993": "template:d07479ff-1172-4464-996d-32d6c7358979",
"5123": "template:280ac289-c726-4cb1-8a11-9ae1c987b399"
};
I was trying to use lodash some but this will need the property name, but I cannot provide the property here such as 5871
You can use Object.values() to get a list of all values in your Object. This will return an array of all values. Then you simply use includes function on the array to see if that values exists in the array.
const obj= {
"5871": "template:d51a08fe-fb60-4da0-841b-03a732f19576",
"6993": "template:d07479ff-1172-4464-996d-32d6c7358979",
"5123": "template:280ac289-c726-4cb1-8a11-9ae1c987b399"
};
const valueExists = Object.values(obj).includes("template:d07479ff-1172-4464-996d-32d6c7358979")
console.log(valueExists)
Lodash's _.includes() works with objects as well as arrays. In addition, you can use _.findKey() to, well, find the key:
const obj = {
"5871": "template:d51a08fe-fb60-4da0-841b-03a732f19576",
"6993": "template:d07479ff-1172-4464-996d-32d6c7358979",
"5123": "template:280ac289-c726-4cb1-8a11-9ae1c987b399"
};
const exists = _.includes(obj, 'template:d07479ff-1172-4464-996d-32d6c7358979')
const key = _.findKey(obj, v => v === 'template:d07479ff-1172-4464-996d-32d6c7358979')
console.log({ exists, key })
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.js"></script>

Role hierarchy mapper / generator (recursive)

I would like to create an object of arrays converting the single level key - (string) value relation to key - (array) keys collection.
Basically, the code must collect other keys and their values recursively starting from collecting self. At the end the object must be like this;
{
ROLE_SUPER_ADMIN: [
'ROLE_SUPER_ADMIN',
'ROLE_ADMIN',
'ROLE_MODERATOR',
'ROLE_AUTHOR'
]
}
What i have achieved yet is;
export const roles = {
ROLE_SUPER_ADMIN: 'ROLE_ADMIN',
ROLE_ADMIN: 'ROLE_MODERATOR',
ROLE_MODERATOR: 'ROLE_AUTHOR',
ROLE_AUTHOR: null,
ROLE_CLIENT: null
}
export function roleMapper() {
const roleArray = {}
const mapper = (key) => {
roleArray[key] = [key];
if (!roles[key] || Array.isArray(roles[key])) {
return;
} else if (!roles[roles[key]]) {
roleArray[key].push(roles[key])
} else {
if (roleArray.hasOwnProperty(key)) {
Object.keys(roles).filter(r => r !== key).forEach((role) => {
roleArray[key].push(mapper(role))
})
}
}
}
Object.keys(roles).forEach((key) => {
mapper(key)
});
console.log(roleArray);
}
I have completely lost solving this. Please help, thanks.
I would use a function generator for this, taking advantage of the easy recursion approach and taking advantage of Object.entries combined with Array.map.
The below method acquires all the siblings of a defined key from an object, assuming that each key value may be the child of the said key.
As a side note, you could technically do that in many other ways (without relying on function generators), I just think that the generator approach is clever and easier to maintain. Moreover, it allows you to re-use the method later and allows you to eventually iterate the values.
Code explanation is directly in the code below.
const roles = {
ROLE_SUPER_ADMIN: 'ROLE_ADMIN',
ROLE_ADMIN: 'ROLE_MODERATOR',
ROLE_MODERATOR: 'ROLE_AUTHOR',
ROLE_AUTHOR: null,
ROLE_CLIENT: null
}
// Acquire all the siblings, where a sibling is a key whose value is the value of another key.
function* getSiblings(v, source) {
// if the desired key exists in source..
if (source[v]) {
// yield the value, which is a role in that case.
yield source[v];
// next, yield all the siblings of that value (role).
yield* [...getSiblings(source[v], source)];
}
}
// Map all roles by its siblings.
const res = Object.entries(roles).map(([key, role]) => {
// key is the main role, whereas role is the "child" role.
// Technically, [key] is not exactly a child role of [key], so we're injecting it manually below to avoid polluting the getSiblings method.
return {
[key]: [key, ...getSiblings(key, roles)] // <-- as mentioned above, the array is build by starting from the main role (key) and appending the child roles (siblings). [key] is a shorthand to set the key.
}
});
console.log(res);
I would separate out the recursive call necessary to fetch the list from the code that builds the output. That allows you to make both of them quite simple:
const listRoles = (rolls, name) => name in roles
? [name, ... listRoles (rolls, roles [name] )]
: []
const roleMapper = (roles) => Object .assign (
... Object.keys (roles) .map (name => ({ [name]: listRoles (roles, name) }))
)
const roles = {ROLE_SUPER_ADMIN: 'ROLE_ADMIN', ROLE_ADMIN: 'ROLE_MODERATOR', ROLE_MODERATOR: 'ROLE_AUTHOR', ROLE_AUTHOR: null, ROLE_CLIENT: null}
console .log (
roleMapper (roles)
)
Here listRoles is the recursive bit, and it simply takes a roles object and a name and returns all the descendant names, so
listRoles(roles, 'ROLE_MODERATOR') //=> ["ROLE_MODERATOR", "ROLE_AUTHOR"]
roleMapper uses that function. It takes the roles object and calls listRoles on each of its keys, combining them into a new object.
Together, these yield the following output:
{
ROLE_SUPER_ADMIN: ["ROLE_SUPER_ADMIN", "ROLE_ADMIN", "ROLE_MODERATOR", "ROLE_AUTHOR"],
ROLE_ADMIN: ["ROLE_ADMIN", "ROLE_MODERATOR", "ROLE_AUTHOR"],
ROLE_MODERATOR: ["ROLE_MODERATOR", "ROLE_AUTHOR"],
ROLE_AUTHOR: ["ROLE_AUTHOR"],
ROLE_CLIENT: ["ROLE_CLIENT"]
}
I see the accepted answer generates a structure more like this:
[
{ROLE_SUPER_ADMIN: ["ROLE_SUPER_ADMIN", "ROLE_ADMIN", "ROLE_MODERATOR", "ROLE_AUTHOR"]},
{ROLE_ADMIN: ["ROLE_ADMIN", "ROLE_MODERATOR", "ROLE_AUTHOR"]},
{ROLE_MODERATOR: ["ROLE_MODERATOR", "ROLE_AUTHOR"]},
{ROLE_AUTHOR: ["ROLE_AUTHOR"]},
{ROLE_CLIENT: ["ROLE_CLIENT"]}
]
(The difference is that mine was a single object, versus this one which was an array of single-property objects.)
While that feels less logical to me, it would be even easier to write:
const roleMapper = (roles) => Object.keys (roles) .map (n => ({ [n]: listRoles (roles, n) }))

Object.entries() alternative for Internet Explorer and ReactJS

Well, I've been building a web application for a couple weeks now, and everything good. I got to the part that I had to test in Internet Explorer, and of all of the things that came up (all fixed except for one), Object.entries() is not supported.
I've been doing some research and try to come up with a simple alternative, but no luck at all.
To be more specific, I'm bringing an object from an API, to fill options for <select></select> fields I have to filter some information, just like this:
Object.entries(this.state.filterInfo.sectorId).map(this.eachOption)
// Function
eachOption = ([key, val], i) => {
return(
<option value={val} key={i}>{val}</option>
);
}
So everything works correctly except for Internet Explorer. The thing is that in this particular component I'm rendering over 30 <select></select> fields. IF there is a solution that doesn't require me to rebuild everything, it would be amazing.
Is there a simple solution? A way around this?
Thanks in advance.
The usual first item to research when you want to use a newer API in an older browser is whether there is a simple polyfill. And, sure enough there is a pretty simple polyfill for Object.entries() shown on the MDN doc site:
if (!Object.entries)
Object.entries = function( obj ){
var ownProps = Object.keys( obj ),
i = ownProps.length,
resArray = new Array(i); // preallocate the Array
while (i--)
resArray[i] = [ownProps[i], obj[ownProps[i]]];
return resArray;
};
Nothing necessarily new to the above answer, but just different code to accomplish the same thing.
Hopefully this helps anyone who stumbles upon this.
// Another approach
const entriesPolyFill = (obj) => Object.keys(obj).map(key => [key, obj[key]]);
// Same thing but easier to read
function entriesPolyFill(obj) {
const keys = Object.keys(obj);
const keyValuePairs = keys.map(key => {
const value = obj[key];
return [key, value];
});
return keyValuePairs;
};
// Possible usage if you don't want to alter Object class:
// Ex: Need key-value pairs to iterate over
const entries = (Object.entries ? Object.entries(obj) : entriesPolyFill(obj));
// Then do whatever you want with the Array
// ---> entries.map(), entries.filter(), etc..
// You could also move the whole thing to a function
// and always call the function so you don't have to
// write excess ternary operators in your code:
// -- In a utils file somewhere else...
export function getEntries(obj) {
return Object.entries ? Object.entries(obj) : Object.keys(obj).map(key => [key, obj[key]]);
}
// Wherever you need to get entries in you project
import { getEntries } from "<path to utils>";
...
const entries = getEntries(obj);
import 'core-js/es7/object';
This works for me.
Here's a concise polyfill using Array.prototype.reduce in a rather clever way:
if(!Object.entries)
Object.entries = function(obj) {
return Object.keys(obj).reduce(function(arr, key) {
arr.push([key, obj[key]]);
return arr;
}, []);
}
Use shim/polyfill like this one: https://github.com/es-shims/Object.entries

AngularJS looping an object to build an array of objects

I am a newbie in JavaScript and AngularJS. I was trying out looping a object, get its key-value pair and then use it to build an array of new objects.
var actorMovie = {
"Leonardo DiCaprio" : "The Revenant",
"Christian Bale" : "The Dark Knight Rises",
"Sylvester Stallone" : "Rocky"
};
if(actorMovie){
var actorMovieArray = [];
angular.forEach(actorMovie, function(value, key) {
actorMovieArray.push ({key: {
"Movies": {
"Best Movie": value
}
}});
});
}
console.log(actorMovieArray);
This console log prints out the right values, but the key remains as 'key' and never updated to the actor's name as expected.
What am I doing wrong here? I tried searching for an answer but did not find any solution. Am I missing something?
I would do something like
angular.forEach(actorMovie, function(value, key) {
actorMovieArray[key]= {
"Movies": {
"Best Movie": value
}
};
});
In your code, javascript does not know that you want to evaluate the key variable to assign the property, and considers the key to be the key string.
As #Hugues pointed out, there is no way for JavaScript to know if you mean the key as literal or as variable, so the literal value is used.
Please be aware that the answer does not behave the same way as you wanted to in your question. Using the key as an array identifier has two drawbacks:
the order of the items when iterating over the keys cannot be retained
if there are two items having the same key (here the actor name), you will only get one in the result as you are overwriting some previously added value. (this is not really the case as your input already is an object literal so that duplicate keys are no concern, but could be a problem when switching to some other input, e.g. an array of items)
This could be okay for you as long as order doesn't matter and you know your keys are unique. If you want the structure as defined in your question, please consider the following snippet:
function buildItem(value, key) {
var res = {};
res[key] = {
"Movies": {
"Best Movie": value
}
};
return res;
}
if(actorMovie){
var actorMovieArray = [];
angular.forEach(actorMovie, function(value, key) {
actorMovieArray.push(buildItem(value, key));
});
}
Try out this jsbin: http://jsbin.com/luborejini/edit?js,console
I would use Object.keys and Array.forEach on the resulting array. And I would also embrace Javascript's functional nature. That way you could easily pull out the creator function into factory mapping libraries for your api json data.
if(actorMovie){
var actorMovieArray = [];
Object.keys(actorMovie).forEach(function(actor){
actorMovieArray[actor] = function(){
return {
Movies: {
BestMovie: actorMovie[actor]
}
};
}();
});
}
I would also recommend not using the actor name as the key in the array. I would rather map it to a model structure, it will make your views / controllers cleaner and easier to understand:
if(actorMovie){
var actorMovieArray = [];
Object.keys(actorMovie).forEach(function(actor) {
actorMovieArray.push(function(){
return {
Actor: actor,
Movies: {
BestMovie: actorMovie[actor]
}
};
}());
});
}
This will drive you into more concise view models, and set you up for easy refactoring once your structure is in place. It will also make testing easier, at least in my opinion.

LoDash _.has for multiple keys

Is there a method or a chain of methods to check if an array of keys exists in an object available in lodash, rather than using the following?
var params = {...}
var isCompleteForm = true;
var requiredKeys = ['firstname', 'lastname', 'email']
for (var i in requiredKeys) {
if (_.has(params, requiredKeys[i]) == false) {
isCompleteForm = false;
break;
}
}
if (isCompleteForm) {
// do something fun
}
UPDATE
Thanks everyone for the awesome solutions! If you're interested, here's the jsPerf of the different solutions.
http://jsperf.com/check-array-of-keys-for-object
I know the question is about lodash, but this can be done with vanilla JS, and it is much faster:
requiredKeys.every(function(k) { return k in params; })
and even cleaner in ES2015:
requiredKeys.every(k => k in params)
You can totally go functional, with every, has and partialfunctions, like this
var requiredKeys = ['firstname', 'lastname', 'email'],
params = {
"firstname": "thefourtheye",
"lastname": "thefourtheye",
"email": "NONE"
};
console.log(_.every(requiredKeys, _.partial(_.has, params)));
// true
We pass a partial function object to _.every, which is actually _.has partially applied to params object. _.every will iterate requiredKeys array and pass the current value to the partial object, which will apply the current value to the partial _.has function and will return true or false. _.every will return true only if all the elements in the array returns true when passed to the function object. In the example I have shown above, since all the keys are in params, it returns true. Even if a single element is not present in that, it will return false.
_(requiredKeys).difference(_(params).keys().value()).empty()
I believe. The key step is getting everything into arrays then working with sets.
or
_requiredKeys.map(_.pluck(params).bind(_)).compact().empty()
Might work.
Assuming that params can have more properties than is required...
var keys = _.keys(params);
var isCompleteForm = requiredKeys.every(function (key) {
return keys.indexOf(key) != -1;
});
Should do the trick.

Categories

Resources