Object.entries() alternative for Internet Explorer and ReactJS - javascript

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

Related

Best way to create a deep cloned object literal from an object with accessors?

I have an object that is a combination of literals and accessors:
const obj = {
stuff: [],
get processedStuff() { return this.stuff.map(el => `${el}!`) }
}
obj.stuff = ['woot']
console.log(obj.processedStuff) // ['woot!']
I want to create a deepClone of obj so that the clone behaves entirely like a literal. So in the clone, changes to stuff will no longer result in changes to processedStuff:
const obj2 = cl0n3Me(obj)
obj2.stuff = ['nope']
console.log(obj.processedStuff) // ['woot!']
Using a library function like cloneDeep in lodash doesn't do this -- the accessors come along for the ride and are a part of the new obj.
I can do this with the following...
const obj2 = JSON.parse(JSON.stringify(obj))
...however I am not sure if that is this the most efficient way / recommended way to do this.
What say ye javascript masters?
You can do a deep clone with using destructuring assignment. afaik, there are no downsides but could be wrong. in my testing it works for your example. it should be noted that this is not a complete deep cloning solution, as it won't handle Date or Map cloning without additional code to handle those types.
function clone( o ) {
if(Array.isArray(o)){
const o2 = [...o];
for(let i=0;i<o2.length;i++){
if(typeof o2[i]==='object'&&o2[i]!==null){
o2[i]=clone(o2[i]);
}
}
return o2;
}else{
const o2 = {...o};
for(let k in o2){
if(typeof o2[k]==='object'&&o2[k]!==null){
o2[k]=clone(o2[k]);
}
}
return o2;
}
}

Returning Key:Val pair from Object Array

I'm trying to create a dictionary of properties from an object array using arrow functions. So far I've tried a few variations of this:
let scVars = {};
scVars = sheetNamedRanges.map(x => (
{
{x.getName()}: {x.getRange().getValue()}
}
));
But I'm getting errors. I know this is a formatting issue but I'm a bit stuck so any help would be great
Array.map will return another Array but what you probably want is to create a new object. Try it like this:
let scVars = {};
sheetNamedRanges.forEach(x => {
scVars[x.getName()] = x.getValue()
});

Transforming array into a object in 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);

JS rename an object key, while preserving its position in the object

My javascript object looks like this:
const someObj = {
arr1: ["str1", "str2"],
arr2: ["str3", "str4"]
}
In attempting to rename a key (e.g. arr1), I end up deleting the existing key and writing a new key with the original value. The order of obj changes.
someObj = {
arr2: ["str3", "str4"],
renamedarr1: ["str1", "str2"]
}
How do I rename a key while preserving the key order?
In the end it was solved in a js-vanila way rather than a react way.
In case somebody would look for a similar solution, I am posting the code I ended up using. Inspired by Luke's idea:
const renameObjKey = ({oldObj, oldKey, newKey}) => {
const keys = Object.keys(oldObj);
const newObj = keys.reduce((acc, val)=>{
if(val === oldKey){
acc[newKey] = oldObj[oldKey];
}
else {
acc[val] = oldObj[val];
}
return acc;
}, {});
return newObj;
};
You might want to consider reducing the array of keys into a new object.
To do this, you need also to know which key changed to what.
Reduce the array of keys
use a reducer which checks for a key change, and change it if necessary.
add the key to the object with the value
After that you have a Object with the order you had before, and possibly a changed key is still at the same position
Something like this might work (not tested)
const changedKeyMap = {"previousKey": "newKey"};
const keys = Object.keys(this.state.obj);
const content = e.target.value;
const result = keys.reduce((acc, val) => {
// modify key, if necessary
if (!!changedKeyMap[val]) {
val = changedKeyMap[val];
}
acc[val] = content;
// or acc[val] = this.state.obj[val] ?
return acc;
}, {});
As you can see, you need to keep track of how you changed a key (changedKeyMap).
The reduce function just iterates over all keys in correct order and adds them to a newly created object. if a key is changed, you can check it in the changedKeyMap and replace it. It will still be at the correct position

How to convert a object array into string in JavaScript?

The array is this:
[{name:'name1',id:'id1',val:'001},
...
{name:'name10',id:'id10',val:'010}]
We want to store in the database as a long text string:
name1,id1,001;...;name10,id10,010
values separated by semicolon and comma.
Is there any convenient join or something else to achieve it? (I guess there must be something better than for loop.)
function joinValues(arr, j1, j2) {
return arr.map(function(o) {
return Object.keys(o).map(function(k) {
return o[k];
}).join(j1)
}).join(j2);
}
var obj = [{a:1,b:2},{a:'x',b:'y'}];
joinValues(obj, ',', ';'); // => "1,2;x,y"
array.map(function(o){
var values = Object.keys(o).map(function(key){
return o[key];
});
return values.join(',');
}).reduce(function(a,b){
return a + ';' + b;
});
Beware there might be compat issues depending on your platform (map, reduce, Object.keys not available everywhere yet!)
Also, take into account that properties order in regular objects is not guaranteed. With this approach, theoretically you could end up having name1,id1,001;...;id10,name10,010. That might be an issue when mapping the string back to objects.
I'm afraid there isn't a built-in language feature for that, but if you can use ES6, it can look quite elegant
Object.values = obj => Object.keys(obj).map(key => obj[key]);
// so to convert your data
data.map(o => Object.values(o).join(',')).join(';');

Categories

Resources