Efficiently fill values in an array of objects in JavaScript - javascript

I have an array of objects like :
a = [{"a":1,"b":2},{"a":3,"c":5}]
I would like to obtain, in an efficient way :
b = [{"a":1,"b":2,"c":"blabla"},{"a":3,"b":"blabla","c":5}]
My function so far is (using the underscoreJS library in the first line):
let fillFuncArr = (res,fill) => {
// we fill the potential missing values
let col_names = _.uniq(res.map(x => Object.keys(x)).reduce((acc, curr) => acc.concat(curr), []), false);
for (let i = 0; i < res.length; i++) {
for (let j = 0; j < col_names.length; j++) {
if (!res[i].hasOwnProperty(col_names[j])) {
res[i][col_names[j]] = fill;
}
}
}
return res;
};
In my example above you would do :
b = fillFuncArr(a,"blabla")
How to make that function faster if at all possible ?
===========================================================================
EDIT after answers to benchmark replies :
I tested the functions like this :
for (let i=0;i<num_tries;i++) {
let time_tmp = performance.now();
console.time("old");
fillFuncArr(obj,"blah");
console.timeEnd("old");
time_old.push(performance.now()-time_tmp);
time_tmp = performance.now();
console.time("new");
fillFuncArrNew(obj,"blah");
console.timeEnd("new");
time_new.push(performance.now()-time_tmp);
}
This answer (the first calculation of the old function is always much faster than the subsequent ones, not quite sure why...) is 50-100 times faster. The fill time is the same, it's getting the keys that makes up all the speed gains :
"old": [
147.52006196975708,
1065.4309248924255,
1023.5124139785767,
1021.830512046814,
1855.5670911073685,
1006.7114781141281,
996.8541929721832,
1306.3085260391235
],
"new": [
18.814231991767883,
23.46549105644226,
17.708116054534912,
15.55942702293396,
18.764864921569824,
15.866382002830505,
19.18179702758789,
23.987511038780212
]

Don't know if this is faster, but definitely shorter:
dummy = {a: 'dummy', b: 'dummy', c: 'dummy'}
a = [{"a": 1, "b": 2}, {"a": 3, "c": 5}]
r = a.map(x => ({...dummy, ...x}))
console.log(r)
If you want the dummy to be fully dynamic, then
function fillKeys(a, value) {
let keys = new Set(a.flatMap(Object.keys)),
dummy = Object.fromEntries(
[...keys].map(k => [k, value]));
return a.map(x => ({...dummy, ...x}));
}
//
a = [{"a": 1, "b": 2}, {"a": 3, "c": 5}]
r = fillKeys(a, 'dummy')
console.log(r)
That being said, I'm sure this is actually an XY problem, so it would help to explain us what you're actually doing. For example, if you just want all objects in the list to respond to the same set of keys, that would be much easier (and also faster) with proxies.

First figure out all keys in the array items, create a "default" object filled with the fill as its values, then iterate over the items and spread the default object, then the original object:
const fillFuncArr = (arr, fill) => {
const allKeys = new Set(arr.flatMap(Object.keys));
const defaultObj = {};
for (const key of allKeys) {
defaultObj[key] = fill;
}
return arr.map(obj => ({ ...defaultObj, ...obj }));
};
a = [{"a":1,"b":2},{"a":3,"c":5}]
b = fillFuncArr(a,"blabla")
console.log(b);

Take a look here:
console.time('allTest');
var was = [{a:1, b:2}, {a:3, c:5}];
function blah(array){
var a = [], o, b = 'blabla';
array.forEach(function(w){
o = {};
o.a = 'a' in w ? w.a : b;
o.b = 'b' in w ? w.b : b;
o.c = 'c' in w ? w.c : b;
a.push(o);
});
return a;
}
console.time('test'); console.log(blah(was)); console.timeEnd('test'); console.timeEnd('allTest');
At least this will show you how to test the time.

const a = [{"a":1,"b":2},{"a":3,"c":5}];
const allKeys=[...new Set(a.flatMap(Object.keys))]
const dummyObj=Object.fromEntries(allKeys.map(key => [key, 'blabla']));
console.log(a.map(data => ({...dummyObj, ...data})))

Related

Why is returning only one object in for in loop [duplicate]

I have an object:
myObject = { 'a': 1, 'b': 2, 'c': 3 }
I am looking for a native method, similar to Array.prototype.map that would be used as follows:
newObject = myObject.map(function (value, label) {
return value * value;
});
// newObject is now { 'a': 1, 'b': 4, 'c': 9 }
Does JavaScript have such a map function for objects? (I want this for Node.JS, so I don't care about cross-browser issues.)
There is no native map to the Object object, but how about this:
var myObject = { 'a': 1, 'b': 2, 'c': 3 };
Object.keys(myObject).forEach(function(key, index) {
myObject[key] *= 2;
});
console.log(myObject);
// => { 'a': 2, 'b': 4, 'c': 6 }
But you could easily iterate over an object using for ... in:
var myObject = { 'a': 1, 'b': 2, 'c': 3 };
for (var key in myObject) {
if (myObject.hasOwnProperty(key)) {
myObject[key] *= 2;
}
}
console.log(myObject);
// { 'a': 2, 'b': 4, 'c': 6 }
Update
A lot of people are mentioning that the previous methods do not return a new object, but rather operate on the object itself. For that matter I wanted to add another solution that returns a new object and leaves the original object as it is:
var myObject = { 'a': 1, 'b': 2, 'c': 3 };
// returns a new object with the values at each key mapped using mapFn(value)
function objectMap(object, mapFn) {
return Object.keys(object).reduce(function(result, key) {
result[key] = mapFn(object[key])
return result
}, {})
}
var newObject = objectMap(myObject, function(value) {
return value * 2
})
console.log(newObject);
// => { 'a': 2, 'b': 4, 'c': 6 }
console.log(myObject);
// => { 'a': 1, 'b': 2, 'c': 3 }
Array.prototype.reduce reduces an array to a single value by somewhat merging the previous value with the current. The chain is initialized by an empty object {}. On every iteration a new key of myObject is added with twice the key as the value.
Update
With new ES6 features, there is a more elegant way to express objectMap.
const objectMap = (obj, fn) =>
Object.fromEntries(
Object.entries(obj).map(
([k, v], i) => [k, fn(v, k, i)]
)
)
const myObject = { a: 1, b: 2, c: 3 }
console.log(objectMap(myObject, v => 2 * v))
How about a one-liner in JS ES10 / ES2019 ?
Making use of Object.entries() and Object.fromEntries():
let newObj = Object.fromEntries(Object.entries(obj).map(([k, v]) => [k, v * v]));
The same thing written as a function:
function objMap(obj, func) {
return Object.fromEntries(Object.entries(obj).map(([k, v]) => [k, func(v)]));
}
// To square each value you can call it like this:
let mappedObj = objMap(obj, (x) => x * x);
This function uses recursion to square nested objects as well:
function objMap(obj, func) {
return Object.fromEntries(
Object.entries(obj).map(([k, v]) =>
[k, v === Object(v) ? objMap(v, func) : func(v)]
)
);
}
// To square each value you can call it like this:
let mappedObj = objMap(obj, (x) => x * x);
With ES7 / ES2016 you can't use Objects.fromEntries, but you can achieve the same using Object.assign in combination with spread operators and computed key names syntax:
let newObj = Object.assign({}, ...Object.entries(obj).map(([k, v]) => ({[k]: v * v})));
ES6 / ES2015 Doesn't allow Object.entries, but you could use Object.keys instead:
let newObj = Object.assign({}, ...Object.keys(obj).map(k => ({[k]: obj[k] * obj[k]})));
ES6 also introduced for...of loops, which allow a more imperative style:
let newObj = {}
for (let [k, v] of Object.entries(obj)) {
newObj[k] = v * v;
}
array.reduce()
Instead of Object.fromEntries and Object.assign you can also use reduce for this:
let newObj = Object.entries(obj).reduce((p, [k, v]) => ({ ...p, [k]: v * v }), {});
Inherited properties and the prototype chain:
In some rare situation you may need to map a class-like object which holds properties of an inherited object on its prototype-chain. In such cases Object.keys() and Object.entries() won't work, because these functions do not include the prototype chain.
If you need to map inherited properties, you can use for (key in myObj) {...}.
Here is an example of such situation:
const obj1 = { 'a': 1, 'b': 2, 'c': 3}
const obj2 = Object.create(obj1); // One of multiple ways to inherit an object in JS.
// Here you see how the properties of obj1 sit on the 'prototype' of obj2
console.log(obj2) // Prints: obj2.__proto__ = { 'a': 1, 'b': 2, 'c': 3}
console.log(Object.keys(obj2)); // Prints: an empty Array.
console.log(Object.entries(obj2)); // Prints: an empty Array.
for (let key in obj2) {
console.log(key); // Prints: 'a', 'b', 'c'
}
However, please do me a favor and avoid inheritance. :-)
No native methods, but lodash#mapValues will do the job brilliantly
_.mapValues({ 'a': 1, 'b': 2, 'c': 3} , function(num) { return num * 3; });
// → { 'a': 3, 'b': 6, 'c': 9 }
It's pretty easy to write one:
Object.map = function(o, f, ctx) {
ctx = ctx || this;
var result = {};
Object.keys(o).forEach(function(k) {
result[k] = f.call(ctx, o[k], k, o);
});
return result;
}
with example code:
> o = { a: 1, b: 2, c: 3 };
> r = Object.map(o, function(v, k, o) {
return v * v;
});
> r
{ a : 1, b: 4, c: 9 }
NB: this version also allows you to (optionally) set the this context for the callback, just like the Array method.
EDIT - changed to remove use of Object.prototype, to ensure that it doesn't clash with any existing property named map on the object.
You could use Object.keys and then forEach over the returned array of keys:
var myObject = { 'a': 1, 'b': 2, 'c': 3 },
newObject = {};
Object.keys(myObject).forEach(function (key) {
var value = myObject[key];
newObject[key] = value * value;
});
Or in a more modular fashion:
function map(obj, callback) {
var result = {};
Object.keys(obj).forEach(function (key) {
result[key] = callback.call(obj, obj[key], key, obj);
});
return result;
}
newObject = map(myObject, function(x) { return x * x; });
Note that Object.keys returns an array containing only the object's own enumerable properties, thus it behaves like a for..in loop with a hasOwnProperty check.
This is really annoying, and everyone in the JS community knows it. There should be this functionality:
const obj1 = {a:4, b:7};
const obj2 = Object.map(obj1, (k,v) => v + 5);
console.log(obj1); // {a:4, b:7}
console.log(obj2); // {a:9, b:12}
here is the naïve implementation:
Object.map = function(obj, fn, ctx){
const ret = {};
for(let k of Object.keys(obj)){
ret[k] = fn.call(ctx || null, k, obj[k]);
});
return ret;
};
it is super annoying to have to implement this yourself all the time ;)
If you want something a little more sophisticated, that doesn't interfere with the Object class, try this:
let map = function (obj, fn, ctx) {
return Object.keys(obj).reduce((a, b) => {
a[b] = fn.call(ctx || null, b, obj[b]);
return a;
}, {});
};
const x = map({a: 2, b: 4}, (k,v) => {
return v*2;
});
but it is safe to add this map function to Object, just don't add to Object.prototype.
Object.map = ... // fairly safe
Object.prototype.map ... // not ok
I came here looking to find and answer for mapping an object to an array and got this page as a result. In case you came here looking for the same answer I was, here is how you can map and object to an array.
You can use map to return a new array from the object like so:
var newObject = Object.keys(myObject).map(function(key) {
return myObject[key];
});
Minimal version
ES2017
Object.entries(obj).reduce((a, [k, v]) => (a[k] = v * v, a), {})
↑↑↑↑↑
ES2019
Object.fromEntries(Object.entries(obj).map(([k, v]) => [k, v * v]))
↑↑↑↑↑
JavaScript just got the new Object.fromEntries method.
Example
function mapObject (obj, fn) {
return Object.fromEntries(
Object
.entries(obj)
.map(fn)
)
}
const myObject = { a: 1, b: 2, c: 3 }
const myNewObject = mapObject(myObject, ([key, value]) => ([key, value * value]))
console.log(myNewObject)
Explanation
The code above converts the Object into an nested Array ([[<key>,<value>], ...]) wich you can map over. Object.fromEntries converts the Array back to an Object.
The cool thing about this pattern, is that you can now easily take object keys into account while mapping.
Documentation
Object.fromEntries()
Object.entries()
Browser Support
Object.fromEntries is currently only supported by these browsers/engines, nevertheless there are polyfills available (e.g #babel/polyfill).
The accepted answer has two drawbacks:
It misuses Array.prototype.reduce, because reducing means to change the structure of a composite type, which doesn't happen in this case.
It is not particularly reusable
An ES6/ES2015 functional approach
Please note that all functions are defined in curried form.
// small, reusable auxiliary functions
const keys = o => Object.keys(o);
const assign = (...o) => Object.assign({}, ...o);
const map = f => xs => xs.map(x => f(x));
const mul = y => x => x * y;
const sqr = x => mul(x) (x);
// the actual map function
const omap = f => o => {
o = assign(o); // A
map(x => o[x] = f(o[x])) (keys(o)); // B
return o;
};
// mock data
const o = {"a":1, "b":2, "c":3};
// and run
console.log(omap(sqr) (o));
console.log(omap(mul(10)) (o));
In line A o is reassigned. Since Javascript passes reference values by sharing, a shallow copy of o is generated. We are now able to mutate o within omap without mutating o in the parent scope.
In line B map's return value is ignored, because map performs a mutation of o. Since this side effect remains within omap and isn't visible in the parent scope, it is totally acceptable.
This is not the fastest solution, but a declarative and reusable one. Here is the same implementation as a one-line, succinct but less readable:
const omap = f => o => (o = assign(o), map(x => o[x] = f(o[x])) (keys(o)), o);
Addendum - why are objects not iterable by default?
ES2015 specified the iterator and iterable protocols. But objects are still not iterable and thus not mappable. The reason is the mixing of data and program level.
For maximum performance.
If your object doesn't change often but needs to be iterated on often I suggest using a native Map as a cache.
// example object
var obj = {a: 1, b: 2, c: 'something'};
// caching map
var objMap = new Map(Object.entries(obj));
// fast iteration on Map object
objMap.forEach((item, key) => {
// do something with an item
console.log(key, item);
});
Object.entries already works in Chrome, Edge, Firefox and beta Opera so it's a future-proof feature.
It's from ES7 so polyfill it https://github.com/es-shims/Object.entries for IE where it doesn't work.
You can convert an object to array simply by using the following:
You can convert the object values to an array:
myObject = { 'a': 1, 'b': 2, 'c': 3 };
let valuesArray = Object.values(myObject);
console.log(valuesArray);
You can convert the object keys to an array:
myObject = { 'a': 1, 'b': 2, 'c': 3 };
let keysArray = Object.keys(myObject);
console.log(keysArray);
Now you can perform normal array operations, including the 'map' function
you can use map method and forEach on arrays but if you want to use it on Object then you can use it with twist like below:
Using Javascript (ES6)
var obj = { 'a': 2, 'b': 4, 'c': 6 };
Object.entries(obj).map( v => obj[v[0]] *= v[1] );
console.log(obj); //it will log as {a: 4, b: 16, c: 36}
var obj2 = { 'a': 4, 'b': 8, 'c': 10 };
Object.entries(obj2).forEach( v => obj2[v[0]] *= v[1] );
console.log(obj2); //it will log as {a: 16, b: 64, c: 100}
Using jQuery
var ob = { 'a': 2, 'b': 4, 'c': 6 };
$.map(ob, function (val, key) {
ob[key] *= val;
});
console.log(ob) //it will log as {a: 4, b: 16, c: 36}
Or you can use other loops also like $.each method as below example:
$.each(ob,function (key, value) {
ob[key] *= value;
});
console.log(ob) //it will also log as {a: 4, b: 16, c: 36}
The map function does not exist on the Object.prototype however you can emulate it like so
var myMap = function ( obj, callback ) {
var result = {};
for ( var key in obj ) {
if ( Object.prototype.hasOwnProperty.call( obj, key ) ) {
if ( typeof callback === 'function' ) {
result[ key ] = callback.call( obj, obj[ key ], key, obj );
}
}
}
return result;
};
var myObject = { 'a': 1, 'b': 2, 'c': 3 };
var newObject = myMap( myObject, function ( value, key ) {
return value * value;
});
EDIT: The canonical way using newer JavaScript features is -
const identity = x =>
x
const omap = (f = identity, o = {}) =>
Object.fromEntries(
Object.entries(o).map(([ k, v ]) =>
[ k, f(v) ]
)
)
Where o is some object and f is your mapping function. Or we could say, given a function from a -> b, and an object with values of type a, produce an object with values of type b. As a pseudo type signature -
// omap : (a -> b, { a }) -> { b }
The original answer was written to demonstrate a powerful combinator, mapReduce which allows us to think of our transformation in a different way
m, the mapping function – gives you a chance to transform the incoming element before…
r, the reducing function – this function combines the accumulator with the result of the mapped element
Intuitively, mapReduce creates a new reducer we can plug directly into Array.prototype.reduce. But more importantly, we can implement our object functor implementation omap plainly by utilizing the object monoid, Object.assign and {}.
const identity = x =>
x
const mapReduce = (m, r) =>
(a, x) => r (a, m (x))
const omap = (f = identity, o = {}) =>
Object
.keys (o)
.reduce
( mapReduce
( k => ({ [k]: f (o[k]) })
, Object.assign
)
, {}
)
const square = x =>
x * x
const data =
{ a : 1, b : 2, c : 3 }
console .log (omap (square, data))
// { a : 1, b : 4, c : 9 }
Notice the only part of the program we actually had to write is the mapping implementation itself –
k => ({ [k]: f (o[k]) })
Which says, given a known object o and some key k, construct an object and whose computed property k is the result of calling f on the key's value, o[k].
We get a glimpse of mapReduce's sequencing potential if we first abstract oreduce
// oreduce : (string * a -> string * b, b, { a }) -> { b }
const oreduce = (f = identity, r = null, o = {}) =>
Object
.keys (o)
.reduce
( mapReduce
( k => [ k, o[k] ]
, f
)
, r
)
// omap : (a -> b, {a}) -> {b}
const omap = (f = identity, o = {}) =>
oreduce
( mapReduce
( ([ k, v ]) =>
({ [k]: f (v) })
, Object.assign
)
, {}
, o
)
Everything works the same, but omap can be defined at a higher-level now. Of course the new Object.entries makes this look silly, but the exercise is still important to the learner.
You won't see the full potential of mapReduce here, but I share this answer because it's interesting to see just how many places it can be applied. If you're interested in how it is derived and other ways it could be useful, please see this answer.
Object Mapper in TypeScript
I like the examples that use Object.fromEntries such as this one, but still, they are not very easy to use. The answers that use Object.keys and then look up the key are actually doing multiple look-ups that may not be necessary.
I wished there was an Object.map function, but we can create our own and call it objectMap with the ability to modify both key and value:
Usage (JavaScript):
const myObject = { 'a': 1, 'b': 2, 'c': 3 };
// keep the key and modify the value
let obj = objectMap(myObject, val => val * 2);
// obj = { a: 2, b: 4, c: 6 }
// modify both key and value
obj = objectMap(myObject,
val => val * 2 + '',
key => (key + key).toUpperCase());
// obj = { AA: '2', BB: '4', CC: '6' }
Code (TypeScript):
interface Dictionary<T> {
[key: string]: T;
}
function objectMap<TValue, TResult>(
obj: Dictionary<TValue>,
valSelector: (val: TValue, obj: Dictionary<TValue>) => TResult,
keySelector?: (key: string, obj: Dictionary<TValue>) => string,
ctx?: Dictionary<TValue>
) {
const ret = {} as Dictionary<TResult>;
for (const key of Object.keys(obj)) {
const retKey = keySelector
? keySelector.call(ctx || null, key, obj)
: key;
const retVal = valSelector.call(ctx || null, obj[key], obj);
ret[retKey] = retVal;
}
return ret;
}
If you are not using TypeScript then copy the above code in TypeScript Playground to get the JavaScript code.
Also, the reason I put keySelector after valSelector in the parameter list, is because it is optional.
* Some credit go to alexander-mills' answer.
Based on #Amberlamps answer, here's a utility function
(as a comment it looked ugly)
function mapObject(obj, mapFunc){
return Object.keys(obj).reduce(function(newObj, value) {
newObj[value] = mapFunc(obj[value]);
return newObj;
}, {});
}
and the use is:
var obj = {a:1, b:3, c:5}
function double(x){return x * 2}
var newObj = mapObject(obj, double);
//=> {a: 2, b: 6, c: 10}
I came upon this as a first-item in a Google search trying to learn to do this, and thought I would share for other folsk finding this recently the solution I found, which uses the npm package immutable.
I think its interesting to share because immutable uses the OP's EXACT situation in their own documentation - the following is not my own code but pulled from the current immutable-js documentation:
const { Seq } = require('immutable')
const myObject = { a: 1, b: 2, c: 3 }
Seq(myObject).map(x => x * x).toObject();
// { a: 1, b: 4, c: 9 }
Not that Seq has other properties ("Seq describes a lazy operation, allowing them to efficiently chain use of all the higher-order collection methods (such as map and filter) by not creating intermediate collections") and that some other immutable-js data structures might also do the job quite efficiently.
Anyone using this method will of course have to npm install immutable and might want to read the docs:
https://facebook.github.io/immutable-js/
My response is largely based off the highest rated response here and hopefully everyone understands (have the same explanation on my GitHub, too). This is why his impementation with map works:
Object.keys(images).map((key) => images[key] = 'url(' + '"' + images[key] + '"' +
')');
The purpose of the function is to take an object and modify the original contents of the object using a method available to all objects (objects and arrays alike) without returning an array. Almost everything within JS is an object, and for that reason elements further down the pipeline of inheritance can potentially technically use those available to those up the line (and the reverse it appears).
The reason that this works is due to the .map functions returning an array REQUIRING that you provide an explicit or implicit RETURN of an array instead of simply modifying an existing object. You essentially trick the program into thinking the object is an array by using Object.keys which will allow you to use the map function with its acting on the values the individual keys are associated with (I actually accidentally returned arrays but fixed it). As long as there isn't a return in the normal sense, there will be no array created with the original object stil intact and modified as programmed.
This particular program takes an object called images and takes the values of its keys and appends url tags for use within another function. Original is this:
var images = {
snow: 'https://www.trbimg.com/img-5aa059f5/turbine/bs-md-weather-20180305',
sunny: 'http://www.cubaweather.org/images/weather-photos/large/Sunny-morning-east-
Matanzas-city- Cuba-20170131-1080.jpg',
rain: 'https://i.pinimg.com/originals/23/d8
/ab/23d8ab1eebc72a123cebc80ce32b43d8.jpg' };
...and modified is this:
var images = {
snow: url('https://www.trbimg.com/img-5aa059f5/turbine/bs-md-weather-20180305'),
sunny: url('http://www.cubaweather.org/images/weather-photos/large/Sunny-morning-
east-Matanzas-city- Cuba-20170131-1080.jpg'),
rain: url('https://i.pinimg.com/originals/23/d8
/ab/23d8ab1eebc72a123cebc80ce32b43d8.jpg')
};
The object's original structure is left intact allowing for normal property access as long as there isn't a return. Do NOT have it return an array like normal and everything will be fine. The goal is REASSIGNING the original values (images[key]) to what is wanted and not anything else. As far as I know, in order to prevent array output there HAS to be REASSIGNMENT of images[key] and no implicit or explicit request to return an array (variable assignment does this and was glitching back and forth for me).
EDIT:
Going to address his other method regarding new object creation to avoid modifying original object (and reassignment appears to still be necessary in order to avoid accidentally creating an array as output). These functions use arrow syntax and are if you simply want to create a new object for future use.
const mapper = (obj, mapFn) => Object.keys(obj).reduce((result, key) => {
result[key] = mapFn(obj)[key];
return result;
}, {});
var newImages = mapper(images, (value) => value);
The way these functions work is like so:
mapFn takes the function to be added later (in this case (value) => value) and simply returns whatever is stored there as a value for that key (or multiplied by two if you change the return value like he did) in mapFn(obj)[key],
and then redefines the original value associated with the key in result[key] = mapFn(obj)[key]
and returns the operation performed on result (the accumulator located in the brackets initiated at the end of the .reduce function).
All of this is being performed on the chosen object and STILL there CANNOT be an implicit request for a returned array and only works when reassigning values as far as I can tell. This requires some mental gymnastics but reduces the lines of code needed as can be seen above. Output is exactly the same as can be seen below:
{snow: "https://www.trbimg.com/img-5aa059f5/turbine/bs-
md-weather-20180305", sunny: "http://www.cubaweather.org/images/weather-
photos/l…morning-east-Matanzas-city-Cuba-20170131-1080.jpg", rain:
"https://i.pinimg.com/originals/23/d8
/ab/23d8ab1eebc72a123cebc80ce32b43d8.jpg"}
Keep in mind this worked with NON-NUMBERS. You CAN duplicate ANY object by SIMPLY RETURNING THE VALUE in the mapFN function.
const mapObject = (targetObject, callbackFn) => {
if (!targetObject) return targetObject;
if (Array.isArray(targetObject)){
return targetObject.map((v)=>mapObject(v, callbackFn))
}
return Object.entries(targetObject).reduce((acc,[key, value]) => {
const res = callbackFn(key, value);
if (!Array.isArray(res) && typeof res ==='object'){
return {...acc, [key]: mapObject(res, callbackFn)}
}
if (Array.isArray(res)){
return {...acc, [key]: res.map((v)=>mapObject(v, callbackFn))}
}
return {...acc, [key]: res};
},{})
};
const mapped = mapObject(a,(key,value)=> {
if (!Array.isArray(value) && key === 'a') return ;
if (!Array.isArray(value) && key === 'e') return [];
if (!Array.isArray(value) && key === 'g') return value * value;
return value;
});
console.log(JSON.stringify(mapped));
// {"b":2,"c":[{"d":2,"e":[],"f":[{"g":4}]}]}
This function goes recursively through the object and arrays of objects. Attributes can be deleted if returned undefined
I needed a version that allowed modifying the keys as well (based on #Amberlamps and #yonatanmn answers);
var facts = [ // can be an object or array - see jsfiddle below
{uuid:"asdfasdf",color:"red"},
{uuid:"sdfgsdfg",color:"green"},
{uuid:"dfghdfgh",color:"blue"}
];
var factObject = mapObject({}, facts, function(key, item) {
return [item.uuid, {test:item.color, oldKey:key}];
});
function mapObject(empty, obj, mapFunc){
return Object.keys(obj).reduce(function(newObj, key) {
var kvPair = mapFunc(key, obj[key]);
newObj[kvPair[0]] = kvPair[1];
return newObj;
}, empty);
}
factObject=
{
"asdfasdf": {"color":"red","oldKey":"0"},
"sdfgsdfg": {"color":"green","oldKey":"1"},
"dfghdfgh": {"color":"blue","oldKey":"2"}
}
Edit: slight change to pass in the starting object {}. Allows it to be [] (if the keys are integers)
var myObject = { 'a': 1, 'b': 2, 'c': 3 };
Object.prototype.map = function(fn){
var oReturn = {};
for (sCurObjectPropertyName in this) {
oReturn[sCurObjectPropertyName] = fn(this[sCurObjectPropertyName], sCurObjectPropertyName);
}
return oReturn;
}
Object.defineProperty(Object.prototype,'map',{enumerable:false});
newObject = myObject.map(function (value, label) {
return value * value;
});
// newObject is now { 'a': 1, 'b': 4, 'c': 9 }
If anyone was looking for a simple solution that maps an object to a new object or to an array:
// Maps an object to a new object by applying a function to each key+value pair.
// Takes the object to map and a function from (key, value) to mapped value.
const mapObject = (obj, fn) => {
const newObj = {};
Object.keys(obj).forEach(k => { newObj[k] = fn(k, obj[k]); });
return newObj;
};
// Maps an object to a new array by applying a function to each key+value pair.
// Takes the object to map and a function from (key, value) to mapped value.
const mapObjectToArray = (obj, fn) => (
Object.keys(obj).map(k => fn(k, obj[k]))
);
This may not work for all objects or all mapping functions, but it works for plain shallow objects and straightforward mapping functions which is all I needed.
To responds more closely to what precisely the OP asked for, the OP wants an object:
myObject = { 'a': 1, 'b': 2, 'c': 3 }
to have a map method myObject.map,
similar to Array.prototype.map that would be used as follows:
newObject = myObject.map(function (value, label) {
return value * value;
});
// newObject is now { 'a': 1, 'b': 4, 'c': 9 }
The imho best (measured in terms to "close to what is asked" + "no ES{5,6,7} required needlessly") answer would be:
myObject.map = function mapForObject(callback)
{
var result = {};
for(var property in this){
if(this.hasOwnProperty(property) && property != "map"){
result[property] = callback(this[property],property,this);
}
}
return result;
}
The code above avoids intentionally using any language features, only available in recent ECMAScript editions. With the code above the problem can be solved lke this:
myObject = { 'a': 1, 'b': 2, 'c': 3 };
myObject.map = function mapForObject(callback)
{
var result = {};
for(var property in this){
if(this.hasOwnProperty(property) && property != "map"){
result[property] = callback(this[property],property,this);
}
}
return result;
}
newObject = myObject.map(function (value, label) {
return value * value;
});
console.log("newObject is now",newObject);
alternative test code here
Besides frowned upon by some, it would be a possibility to insert the solution in the prototype chain like this.
Object.prototype.map = function(callback)
{
var result = {};
for(var property in this){
if(this.hasOwnProperty(property)){
result[property] = callback(this[property],property,this);
}
}
return result;
}
Something, which when done with careful oversight should not have any ill effects and not impact map method of other objects (i.e. Array's map).
First, convert your HTMLCollection using Object.entries(collection). Then it’s an iterable you can now use the .map method on it.
Object.entries(collection).map(...)
reference
https://medium.com/#js_tut/calling-javascript-code-on-multiple-div-elements-without-the-id-attribute-97ff6a50f31
I handle only strings to reduce exemptions:
Object.keys(params).map(k => typeof params[k] == "string" ? params[k] = params[k].trim() : null);
If you're interested in mapping not only values but also keys, I have written Object.map(valueMapper, keyMapper), that behaves this way:
var source = { a: 1, b: 2 };
function sum(x) { return x + x }
source.map(sum); // returns { a: 2, b: 4 }
source.map(undefined, sum); // returns { aa: 1, bb: 2 }
source.map(sum, sum); // returns { aa: 2, bb: 4 }
Hey wrote a little mapper function that might help.
function propertyMapper(object, src){
for (var property in object) {
for (var sourceProp in src) {
if(property === sourceProp){
if(Object.prototype.toString.call( property ) === '[object Array]'){
propertyMapper(object[property], src[sourceProp]);
}else{
object[property] = src[sourceProp];
}
}
}
}
}
A different take on it is to use a custom json stringify function that can also work on deep objects. This might be useful if you intend to post it to the server anyway as json
const obj = { 'a': 1, 'b': 2, x: {'c': 3 }}
const json = JSON.stringify(obj, (k, v) => typeof v === 'number' ? v * v : v)
console.log(json)
console.log('back to json:', JSON.parse(json))
I needed a function to optionally map not only (nor exclusively) values, but also keys. The original object should not change. The object contained only primitive values also.
function mappedObject(obj, keyMapper, valueMapper) {
const mapped = {};
const keys = Object.keys(obj);
const mapKey = typeof keyMapper == 'function';
const mapVal = typeof valueMapper == 'function';
for (let i = 0; i < keys.length; i++) {
const key = mapKey ? keyMapper(keys[i]) : keys[i];
const val = mapVal ? valueMapper(obj[keys[i]]) : obj[keys[i]];
mapped[key] = val;
}
return mapped;
}
Use. Pass a keymapper and a valuemapper function:
const o1 = { x: 1, c: 2 }
mappedObject(o1, k => k + '0', v => v + 1) // {x0: 2, c0: 3}

How to map object values in React? [duplicate]

I have an object:
myObject = { 'a': 1, 'b': 2, 'c': 3 }
I am looking for a native method, similar to Array.prototype.map that would be used as follows:
newObject = myObject.map(function (value, label) {
return value * value;
});
// newObject is now { 'a': 1, 'b': 4, 'c': 9 }
Does JavaScript have such a map function for objects? (I want this for Node.JS, so I don't care about cross-browser issues.)
There is no native map to the Object object, but how about this:
var myObject = { 'a': 1, 'b': 2, 'c': 3 };
Object.keys(myObject).forEach(function(key, index) {
myObject[key] *= 2;
});
console.log(myObject);
// => { 'a': 2, 'b': 4, 'c': 6 }
But you could easily iterate over an object using for ... in:
var myObject = { 'a': 1, 'b': 2, 'c': 3 };
for (var key in myObject) {
if (myObject.hasOwnProperty(key)) {
myObject[key] *= 2;
}
}
console.log(myObject);
// { 'a': 2, 'b': 4, 'c': 6 }
Update
A lot of people are mentioning that the previous methods do not return a new object, but rather operate on the object itself. For that matter I wanted to add another solution that returns a new object and leaves the original object as it is:
var myObject = { 'a': 1, 'b': 2, 'c': 3 };
// returns a new object with the values at each key mapped using mapFn(value)
function objectMap(object, mapFn) {
return Object.keys(object).reduce(function(result, key) {
result[key] = mapFn(object[key])
return result
}, {})
}
var newObject = objectMap(myObject, function(value) {
return value * 2
})
console.log(newObject);
// => { 'a': 2, 'b': 4, 'c': 6 }
console.log(myObject);
// => { 'a': 1, 'b': 2, 'c': 3 }
Array.prototype.reduce reduces an array to a single value by somewhat merging the previous value with the current. The chain is initialized by an empty object {}. On every iteration a new key of myObject is added with twice the key as the value.
Update
With new ES6 features, there is a more elegant way to express objectMap.
const objectMap = (obj, fn) =>
Object.fromEntries(
Object.entries(obj).map(
([k, v], i) => [k, fn(v, k, i)]
)
)
const myObject = { a: 1, b: 2, c: 3 }
console.log(objectMap(myObject, v => 2 * v))
How about a one-liner in JS ES10 / ES2019 ?
Making use of Object.entries() and Object.fromEntries():
let newObj = Object.fromEntries(Object.entries(obj).map(([k, v]) => [k, v * v]));
The same thing written as a function:
function objMap(obj, func) {
return Object.fromEntries(Object.entries(obj).map(([k, v]) => [k, func(v)]));
}
// To square each value you can call it like this:
let mappedObj = objMap(obj, (x) => x * x);
This function uses recursion to square nested objects as well:
function objMap(obj, func) {
return Object.fromEntries(
Object.entries(obj).map(([k, v]) =>
[k, v === Object(v) ? objMap(v, func) : func(v)]
)
);
}
// To square each value you can call it like this:
let mappedObj = objMap(obj, (x) => x * x);
With ES7 / ES2016 you can't use Objects.fromEntries, but you can achieve the same using Object.assign in combination with spread operators and computed key names syntax:
let newObj = Object.assign({}, ...Object.entries(obj).map(([k, v]) => ({[k]: v * v})));
ES6 / ES2015 Doesn't allow Object.entries, but you could use Object.keys instead:
let newObj = Object.assign({}, ...Object.keys(obj).map(k => ({[k]: obj[k] * obj[k]})));
ES6 also introduced for...of loops, which allow a more imperative style:
let newObj = {}
for (let [k, v] of Object.entries(obj)) {
newObj[k] = v * v;
}
array.reduce()
Instead of Object.fromEntries and Object.assign you can also use reduce for this:
let newObj = Object.entries(obj).reduce((p, [k, v]) => ({ ...p, [k]: v * v }), {});
Inherited properties and the prototype chain:
In some rare situation you may need to map a class-like object which holds properties of an inherited object on its prototype-chain. In such cases Object.keys() and Object.entries() won't work, because these functions do not include the prototype chain.
If you need to map inherited properties, you can use for (key in myObj) {...}.
Here is an example of such situation:
const obj1 = { 'a': 1, 'b': 2, 'c': 3}
const obj2 = Object.create(obj1); // One of multiple ways to inherit an object in JS.
// Here you see how the properties of obj1 sit on the 'prototype' of obj2
console.log(obj2) // Prints: obj2.__proto__ = { 'a': 1, 'b': 2, 'c': 3}
console.log(Object.keys(obj2)); // Prints: an empty Array.
console.log(Object.entries(obj2)); // Prints: an empty Array.
for (let key in obj2) {
console.log(key); // Prints: 'a', 'b', 'c'
}
However, please do me a favor and avoid inheritance. :-)
No native methods, but lodash#mapValues will do the job brilliantly
_.mapValues({ 'a': 1, 'b': 2, 'c': 3} , function(num) { return num * 3; });
// → { 'a': 3, 'b': 6, 'c': 9 }
It's pretty easy to write one:
Object.map = function(o, f, ctx) {
ctx = ctx || this;
var result = {};
Object.keys(o).forEach(function(k) {
result[k] = f.call(ctx, o[k], k, o);
});
return result;
}
with example code:
> o = { a: 1, b: 2, c: 3 };
> r = Object.map(o, function(v, k, o) {
return v * v;
});
> r
{ a : 1, b: 4, c: 9 }
NB: this version also allows you to (optionally) set the this context for the callback, just like the Array method.
EDIT - changed to remove use of Object.prototype, to ensure that it doesn't clash with any existing property named map on the object.
You could use Object.keys and then forEach over the returned array of keys:
var myObject = { 'a': 1, 'b': 2, 'c': 3 },
newObject = {};
Object.keys(myObject).forEach(function (key) {
var value = myObject[key];
newObject[key] = value * value;
});
Or in a more modular fashion:
function map(obj, callback) {
var result = {};
Object.keys(obj).forEach(function (key) {
result[key] = callback.call(obj, obj[key], key, obj);
});
return result;
}
newObject = map(myObject, function(x) { return x * x; });
Note that Object.keys returns an array containing only the object's own enumerable properties, thus it behaves like a for..in loop with a hasOwnProperty check.
This is really annoying, and everyone in the JS community knows it. There should be this functionality:
const obj1 = {a:4, b:7};
const obj2 = Object.map(obj1, (k,v) => v + 5);
console.log(obj1); // {a:4, b:7}
console.log(obj2); // {a:9, b:12}
here is the naïve implementation:
Object.map = function(obj, fn, ctx){
const ret = {};
for(let k of Object.keys(obj)){
ret[k] = fn.call(ctx || null, k, obj[k]);
});
return ret;
};
it is super annoying to have to implement this yourself all the time ;)
If you want something a little more sophisticated, that doesn't interfere with the Object class, try this:
let map = function (obj, fn, ctx) {
return Object.keys(obj).reduce((a, b) => {
a[b] = fn.call(ctx || null, b, obj[b]);
return a;
}, {});
};
const x = map({a: 2, b: 4}, (k,v) => {
return v*2;
});
but it is safe to add this map function to Object, just don't add to Object.prototype.
Object.map = ... // fairly safe
Object.prototype.map ... // not ok
I came here looking to find and answer for mapping an object to an array and got this page as a result. In case you came here looking for the same answer I was, here is how you can map and object to an array.
You can use map to return a new array from the object like so:
var newObject = Object.keys(myObject).map(function(key) {
return myObject[key];
});
Minimal version
ES2017
Object.entries(obj).reduce((a, [k, v]) => (a[k] = v * v, a), {})
↑↑↑↑↑
ES2019
Object.fromEntries(Object.entries(obj).map(([k, v]) => [k, v * v]))
↑↑↑↑↑
JavaScript just got the new Object.fromEntries method.
Example
function mapObject (obj, fn) {
return Object.fromEntries(
Object
.entries(obj)
.map(fn)
)
}
const myObject = { a: 1, b: 2, c: 3 }
const myNewObject = mapObject(myObject, ([key, value]) => ([key, value * value]))
console.log(myNewObject)
Explanation
The code above converts the Object into an nested Array ([[<key>,<value>], ...]) wich you can map over. Object.fromEntries converts the Array back to an Object.
The cool thing about this pattern, is that you can now easily take object keys into account while mapping.
Documentation
Object.fromEntries()
Object.entries()
Browser Support
Object.fromEntries is currently only supported by these browsers/engines, nevertheless there are polyfills available (e.g #babel/polyfill).
The accepted answer has two drawbacks:
It misuses Array.prototype.reduce, because reducing means to change the structure of a composite type, which doesn't happen in this case.
It is not particularly reusable
An ES6/ES2015 functional approach
Please note that all functions are defined in curried form.
// small, reusable auxiliary functions
const keys = o => Object.keys(o);
const assign = (...o) => Object.assign({}, ...o);
const map = f => xs => xs.map(x => f(x));
const mul = y => x => x * y;
const sqr = x => mul(x) (x);
// the actual map function
const omap = f => o => {
o = assign(o); // A
map(x => o[x] = f(o[x])) (keys(o)); // B
return o;
};
// mock data
const o = {"a":1, "b":2, "c":3};
// and run
console.log(omap(sqr) (o));
console.log(omap(mul(10)) (o));
In line A o is reassigned. Since Javascript passes reference values by sharing, a shallow copy of o is generated. We are now able to mutate o within omap without mutating o in the parent scope.
In line B map's return value is ignored, because map performs a mutation of o. Since this side effect remains within omap and isn't visible in the parent scope, it is totally acceptable.
This is not the fastest solution, but a declarative and reusable one. Here is the same implementation as a one-line, succinct but less readable:
const omap = f => o => (o = assign(o), map(x => o[x] = f(o[x])) (keys(o)), o);
Addendum - why are objects not iterable by default?
ES2015 specified the iterator and iterable protocols. But objects are still not iterable and thus not mappable. The reason is the mixing of data and program level.
For maximum performance.
If your object doesn't change often but needs to be iterated on often I suggest using a native Map as a cache.
// example object
var obj = {a: 1, b: 2, c: 'something'};
// caching map
var objMap = new Map(Object.entries(obj));
// fast iteration on Map object
objMap.forEach((item, key) => {
// do something with an item
console.log(key, item);
});
Object.entries already works in Chrome, Edge, Firefox and beta Opera so it's a future-proof feature.
It's from ES7 so polyfill it https://github.com/es-shims/Object.entries for IE where it doesn't work.
You can convert an object to array simply by using the following:
You can convert the object values to an array:
myObject = { 'a': 1, 'b': 2, 'c': 3 };
let valuesArray = Object.values(myObject);
console.log(valuesArray);
You can convert the object keys to an array:
myObject = { 'a': 1, 'b': 2, 'c': 3 };
let keysArray = Object.keys(myObject);
console.log(keysArray);
Now you can perform normal array operations, including the 'map' function
you can use map method and forEach on arrays but if you want to use it on Object then you can use it with twist like below:
Using Javascript (ES6)
var obj = { 'a': 2, 'b': 4, 'c': 6 };
Object.entries(obj).map( v => obj[v[0]] *= v[1] );
console.log(obj); //it will log as {a: 4, b: 16, c: 36}
var obj2 = { 'a': 4, 'b': 8, 'c': 10 };
Object.entries(obj2).forEach( v => obj2[v[0]] *= v[1] );
console.log(obj2); //it will log as {a: 16, b: 64, c: 100}
Using jQuery
var ob = { 'a': 2, 'b': 4, 'c': 6 };
$.map(ob, function (val, key) {
ob[key] *= val;
});
console.log(ob) //it will log as {a: 4, b: 16, c: 36}
Or you can use other loops also like $.each method as below example:
$.each(ob,function (key, value) {
ob[key] *= value;
});
console.log(ob) //it will also log as {a: 4, b: 16, c: 36}
The map function does not exist on the Object.prototype however you can emulate it like so
var myMap = function ( obj, callback ) {
var result = {};
for ( var key in obj ) {
if ( Object.prototype.hasOwnProperty.call( obj, key ) ) {
if ( typeof callback === 'function' ) {
result[ key ] = callback.call( obj, obj[ key ], key, obj );
}
}
}
return result;
};
var myObject = { 'a': 1, 'b': 2, 'c': 3 };
var newObject = myMap( myObject, function ( value, key ) {
return value * value;
});
EDIT: The canonical way using newer JavaScript features is -
const identity = x =>
x
const omap = (f = identity, o = {}) =>
Object.fromEntries(
Object.entries(o).map(([ k, v ]) =>
[ k, f(v) ]
)
)
Where o is some object and f is your mapping function. Or we could say, given a function from a -> b, and an object with values of type a, produce an object with values of type b. As a pseudo type signature -
// omap : (a -> b, { a }) -> { b }
The original answer was written to demonstrate a powerful combinator, mapReduce which allows us to think of our transformation in a different way
m, the mapping function – gives you a chance to transform the incoming element before…
r, the reducing function – this function combines the accumulator with the result of the mapped element
Intuitively, mapReduce creates a new reducer we can plug directly into Array.prototype.reduce. But more importantly, we can implement our object functor implementation omap plainly by utilizing the object monoid, Object.assign and {}.
const identity = x =>
x
const mapReduce = (m, r) =>
(a, x) => r (a, m (x))
const omap = (f = identity, o = {}) =>
Object
.keys (o)
.reduce
( mapReduce
( k => ({ [k]: f (o[k]) })
, Object.assign
)
, {}
)
const square = x =>
x * x
const data =
{ a : 1, b : 2, c : 3 }
console .log (omap (square, data))
// { a : 1, b : 4, c : 9 }
Notice the only part of the program we actually had to write is the mapping implementation itself –
k => ({ [k]: f (o[k]) })
Which says, given a known object o and some key k, construct an object and whose computed property k is the result of calling f on the key's value, o[k].
We get a glimpse of mapReduce's sequencing potential if we first abstract oreduce
// oreduce : (string * a -> string * b, b, { a }) -> { b }
const oreduce = (f = identity, r = null, o = {}) =>
Object
.keys (o)
.reduce
( mapReduce
( k => [ k, o[k] ]
, f
)
, r
)
// omap : (a -> b, {a}) -> {b}
const omap = (f = identity, o = {}) =>
oreduce
( mapReduce
( ([ k, v ]) =>
({ [k]: f (v) })
, Object.assign
)
, {}
, o
)
Everything works the same, but omap can be defined at a higher-level now. Of course the new Object.entries makes this look silly, but the exercise is still important to the learner.
You won't see the full potential of mapReduce here, but I share this answer because it's interesting to see just how many places it can be applied. If you're interested in how it is derived and other ways it could be useful, please see this answer.
Object Mapper in TypeScript
I like the examples that use Object.fromEntries such as this one, but still, they are not very easy to use. The answers that use Object.keys and then look up the key are actually doing multiple look-ups that may not be necessary.
I wished there was an Object.map function, but we can create our own and call it objectMap with the ability to modify both key and value:
Usage (JavaScript):
const myObject = { 'a': 1, 'b': 2, 'c': 3 };
// keep the key and modify the value
let obj = objectMap(myObject, val => val * 2);
// obj = { a: 2, b: 4, c: 6 }
// modify both key and value
obj = objectMap(myObject,
val => val * 2 + '',
key => (key + key).toUpperCase());
// obj = { AA: '2', BB: '4', CC: '6' }
Code (TypeScript):
interface Dictionary<T> {
[key: string]: T;
}
function objectMap<TValue, TResult>(
obj: Dictionary<TValue>,
valSelector: (val: TValue, obj: Dictionary<TValue>) => TResult,
keySelector?: (key: string, obj: Dictionary<TValue>) => string,
ctx?: Dictionary<TValue>
) {
const ret = {} as Dictionary<TResult>;
for (const key of Object.keys(obj)) {
const retKey = keySelector
? keySelector.call(ctx || null, key, obj)
: key;
const retVal = valSelector.call(ctx || null, obj[key], obj);
ret[retKey] = retVal;
}
return ret;
}
If you are not using TypeScript then copy the above code in TypeScript Playground to get the JavaScript code.
Also, the reason I put keySelector after valSelector in the parameter list, is because it is optional.
* Some credit go to alexander-mills' answer.
Based on #Amberlamps answer, here's a utility function
(as a comment it looked ugly)
function mapObject(obj, mapFunc){
return Object.keys(obj).reduce(function(newObj, value) {
newObj[value] = mapFunc(obj[value]);
return newObj;
}, {});
}
and the use is:
var obj = {a:1, b:3, c:5}
function double(x){return x * 2}
var newObj = mapObject(obj, double);
//=> {a: 2, b: 6, c: 10}
I came upon this as a first-item in a Google search trying to learn to do this, and thought I would share for other folsk finding this recently the solution I found, which uses the npm package immutable.
I think its interesting to share because immutable uses the OP's EXACT situation in their own documentation - the following is not my own code but pulled from the current immutable-js documentation:
const { Seq } = require('immutable')
const myObject = { a: 1, b: 2, c: 3 }
Seq(myObject).map(x => x * x).toObject();
// { a: 1, b: 4, c: 9 }
Not that Seq has other properties ("Seq describes a lazy operation, allowing them to efficiently chain use of all the higher-order collection methods (such as map and filter) by not creating intermediate collections") and that some other immutable-js data structures might also do the job quite efficiently.
Anyone using this method will of course have to npm install immutable and might want to read the docs:
https://facebook.github.io/immutable-js/
My response is largely based off the highest rated response here and hopefully everyone understands (have the same explanation on my GitHub, too). This is why his impementation with map works:
Object.keys(images).map((key) => images[key] = 'url(' + '"' + images[key] + '"' +
')');
The purpose of the function is to take an object and modify the original contents of the object using a method available to all objects (objects and arrays alike) without returning an array. Almost everything within JS is an object, and for that reason elements further down the pipeline of inheritance can potentially technically use those available to those up the line (and the reverse it appears).
The reason that this works is due to the .map functions returning an array REQUIRING that you provide an explicit or implicit RETURN of an array instead of simply modifying an existing object. You essentially trick the program into thinking the object is an array by using Object.keys which will allow you to use the map function with its acting on the values the individual keys are associated with (I actually accidentally returned arrays but fixed it). As long as there isn't a return in the normal sense, there will be no array created with the original object stil intact and modified as programmed.
This particular program takes an object called images and takes the values of its keys and appends url tags for use within another function. Original is this:
var images = {
snow: 'https://www.trbimg.com/img-5aa059f5/turbine/bs-md-weather-20180305',
sunny: 'http://www.cubaweather.org/images/weather-photos/large/Sunny-morning-east-
Matanzas-city- Cuba-20170131-1080.jpg',
rain: 'https://i.pinimg.com/originals/23/d8
/ab/23d8ab1eebc72a123cebc80ce32b43d8.jpg' };
...and modified is this:
var images = {
snow: url('https://www.trbimg.com/img-5aa059f5/turbine/bs-md-weather-20180305'),
sunny: url('http://www.cubaweather.org/images/weather-photos/large/Sunny-morning-
east-Matanzas-city- Cuba-20170131-1080.jpg'),
rain: url('https://i.pinimg.com/originals/23/d8
/ab/23d8ab1eebc72a123cebc80ce32b43d8.jpg')
};
The object's original structure is left intact allowing for normal property access as long as there isn't a return. Do NOT have it return an array like normal and everything will be fine. The goal is REASSIGNING the original values (images[key]) to what is wanted and not anything else. As far as I know, in order to prevent array output there HAS to be REASSIGNMENT of images[key] and no implicit or explicit request to return an array (variable assignment does this and was glitching back and forth for me).
EDIT:
Going to address his other method regarding new object creation to avoid modifying original object (and reassignment appears to still be necessary in order to avoid accidentally creating an array as output). These functions use arrow syntax and are if you simply want to create a new object for future use.
const mapper = (obj, mapFn) => Object.keys(obj).reduce((result, key) => {
result[key] = mapFn(obj)[key];
return result;
}, {});
var newImages = mapper(images, (value) => value);
The way these functions work is like so:
mapFn takes the function to be added later (in this case (value) => value) and simply returns whatever is stored there as a value for that key (or multiplied by two if you change the return value like he did) in mapFn(obj)[key],
and then redefines the original value associated with the key in result[key] = mapFn(obj)[key]
and returns the operation performed on result (the accumulator located in the brackets initiated at the end of the .reduce function).
All of this is being performed on the chosen object and STILL there CANNOT be an implicit request for a returned array and only works when reassigning values as far as I can tell. This requires some mental gymnastics but reduces the lines of code needed as can be seen above. Output is exactly the same as can be seen below:
{snow: "https://www.trbimg.com/img-5aa059f5/turbine/bs-
md-weather-20180305", sunny: "http://www.cubaweather.org/images/weather-
photos/l…morning-east-Matanzas-city-Cuba-20170131-1080.jpg", rain:
"https://i.pinimg.com/originals/23/d8
/ab/23d8ab1eebc72a123cebc80ce32b43d8.jpg"}
Keep in mind this worked with NON-NUMBERS. You CAN duplicate ANY object by SIMPLY RETURNING THE VALUE in the mapFN function.
const mapObject = (targetObject, callbackFn) => {
if (!targetObject) return targetObject;
if (Array.isArray(targetObject)){
return targetObject.map((v)=>mapObject(v, callbackFn))
}
return Object.entries(targetObject).reduce((acc,[key, value]) => {
const res = callbackFn(key, value);
if (!Array.isArray(res) && typeof res ==='object'){
return {...acc, [key]: mapObject(res, callbackFn)}
}
if (Array.isArray(res)){
return {...acc, [key]: res.map((v)=>mapObject(v, callbackFn))}
}
return {...acc, [key]: res};
},{})
};
const mapped = mapObject(a,(key,value)=> {
if (!Array.isArray(value) && key === 'a') return ;
if (!Array.isArray(value) && key === 'e') return [];
if (!Array.isArray(value) && key === 'g') return value * value;
return value;
});
console.log(JSON.stringify(mapped));
// {"b":2,"c":[{"d":2,"e":[],"f":[{"g":4}]}]}
This function goes recursively through the object and arrays of objects. Attributes can be deleted if returned undefined
I needed a version that allowed modifying the keys as well (based on #Amberlamps and #yonatanmn answers);
var facts = [ // can be an object or array - see jsfiddle below
{uuid:"asdfasdf",color:"red"},
{uuid:"sdfgsdfg",color:"green"},
{uuid:"dfghdfgh",color:"blue"}
];
var factObject = mapObject({}, facts, function(key, item) {
return [item.uuid, {test:item.color, oldKey:key}];
});
function mapObject(empty, obj, mapFunc){
return Object.keys(obj).reduce(function(newObj, key) {
var kvPair = mapFunc(key, obj[key]);
newObj[kvPair[0]] = kvPair[1];
return newObj;
}, empty);
}
factObject=
{
"asdfasdf": {"color":"red","oldKey":"0"},
"sdfgsdfg": {"color":"green","oldKey":"1"},
"dfghdfgh": {"color":"blue","oldKey":"2"}
}
Edit: slight change to pass in the starting object {}. Allows it to be [] (if the keys are integers)
var myObject = { 'a': 1, 'b': 2, 'c': 3 };
Object.prototype.map = function(fn){
var oReturn = {};
for (sCurObjectPropertyName in this) {
oReturn[sCurObjectPropertyName] = fn(this[sCurObjectPropertyName], sCurObjectPropertyName);
}
return oReturn;
}
Object.defineProperty(Object.prototype,'map',{enumerable:false});
newObject = myObject.map(function (value, label) {
return value * value;
});
// newObject is now { 'a': 1, 'b': 4, 'c': 9 }
If anyone was looking for a simple solution that maps an object to a new object or to an array:
// Maps an object to a new object by applying a function to each key+value pair.
// Takes the object to map and a function from (key, value) to mapped value.
const mapObject = (obj, fn) => {
const newObj = {};
Object.keys(obj).forEach(k => { newObj[k] = fn(k, obj[k]); });
return newObj;
};
// Maps an object to a new array by applying a function to each key+value pair.
// Takes the object to map and a function from (key, value) to mapped value.
const mapObjectToArray = (obj, fn) => (
Object.keys(obj).map(k => fn(k, obj[k]))
);
This may not work for all objects or all mapping functions, but it works for plain shallow objects and straightforward mapping functions which is all I needed.
To responds more closely to what precisely the OP asked for, the OP wants an object:
myObject = { 'a': 1, 'b': 2, 'c': 3 }
to have a map method myObject.map,
similar to Array.prototype.map that would be used as follows:
newObject = myObject.map(function (value, label) {
return value * value;
});
// newObject is now { 'a': 1, 'b': 4, 'c': 9 }
The imho best (measured in terms to "close to what is asked" + "no ES{5,6,7} required needlessly") answer would be:
myObject.map = function mapForObject(callback)
{
var result = {};
for(var property in this){
if(this.hasOwnProperty(property) && property != "map"){
result[property] = callback(this[property],property,this);
}
}
return result;
}
The code above avoids intentionally using any language features, only available in recent ECMAScript editions. With the code above the problem can be solved lke this:
myObject = { 'a': 1, 'b': 2, 'c': 3 };
myObject.map = function mapForObject(callback)
{
var result = {};
for(var property in this){
if(this.hasOwnProperty(property) && property != "map"){
result[property] = callback(this[property],property,this);
}
}
return result;
}
newObject = myObject.map(function (value, label) {
return value * value;
});
console.log("newObject is now",newObject);
alternative test code here
Besides frowned upon by some, it would be a possibility to insert the solution in the prototype chain like this.
Object.prototype.map = function(callback)
{
var result = {};
for(var property in this){
if(this.hasOwnProperty(property)){
result[property] = callback(this[property],property,this);
}
}
return result;
}
Something, which when done with careful oversight should not have any ill effects and not impact map method of other objects (i.e. Array's map).
First, convert your HTMLCollection using Object.entries(collection). Then it’s an iterable you can now use the .map method on it.
Object.entries(collection).map(...)
reference
https://medium.com/#js_tut/calling-javascript-code-on-multiple-div-elements-without-the-id-attribute-97ff6a50f31
I handle only strings to reduce exemptions:
Object.keys(params).map(k => typeof params[k] == "string" ? params[k] = params[k].trim() : null);
If you're interested in mapping not only values but also keys, I have written Object.map(valueMapper, keyMapper), that behaves this way:
var source = { a: 1, b: 2 };
function sum(x) { return x + x }
source.map(sum); // returns { a: 2, b: 4 }
source.map(undefined, sum); // returns { aa: 1, bb: 2 }
source.map(sum, sum); // returns { aa: 2, bb: 4 }
Hey wrote a little mapper function that might help.
function propertyMapper(object, src){
for (var property in object) {
for (var sourceProp in src) {
if(property === sourceProp){
if(Object.prototype.toString.call( property ) === '[object Array]'){
propertyMapper(object[property], src[sourceProp]);
}else{
object[property] = src[sourceProp];
}
}
}
}
}
A different take on it is to use a custom json stringify function that can also work on deep objects. This might be useful if you intend to post it to the server anyway as json
const obj = { 'a': 1, 'b': 2, x: {'c': 3 }}
const json = JSON.stringify(obj, (k, v) => typeof v === 'number' ? v * v : v)
console.log(json)
console.log('back to json:', JSON.parse(json))
I needed a function to optionally map not only (nor exclusively) values, but also keys. The original object should not change. The object contained only primitive values also.
function mappedObject(obj, keyMapper, valueMapper) {
const mapped = {};
const keys = Object.keys(obj);
const mapKey = typeof keyMapper == 'function';
const mapVal = typeof valueMapper == 'function';
for (let i = 0; i < keys.length; i++) {
const key = mapKey ? keyMapper(keys[i]) : keys[i];
const val = mapVal ? valueMapper(obj[keys[i]]) : obj[keys[i]];
mapped[key] = val;
}
return mapped;
}
Use. Pass a keymapper and a valuemapper function:
const o1 = { x: 1, c: 2 }
mappedObject(o1, k => k + '0', v => v + 1) // {x0: 2, c0: 3}

Mixing Arrays using JavaScript

What's the best way to mix multiple arrays like the way in the image below,
PS:
I don't know what will be the length of each array
Arrays will contain +10000 elements
There will be more than 3 arrays
I made a solution for it but I'm looking for any better solution
Here's my Own solution, I was looking for any better idea
import { compact, flattenDeep } from "lodash/array";
export const doTheMagic = master => {
const longest = master.reduce((p, c, i, a) => (a[p].length > c.length ? p : i), 0);
const mixed = master[longest].map((i, k) => {
return master.map((o, a) => {
if (master[a][k]) return master[a][k];
});
});
const flaten = flattenDeep(mixed);
return compact(flaten);// to remove falsey values
};
const one = [1,2,3];
const two = ['a','b','c','d','e'];
const three = ['k','l','m','n'];
const mix = doTheMagic([one,two,three]);
console.log('mix: ', mix);
You could use lodash for your solution.
const { flow, zip, flatten, filter} = _
const doTheMagic = flow(
zip,
flatten,
filter
)
const one = [1, 2, 3]
const two = ['😕', '🤯', '🙈', '🙊', '🙉', '😃']
const three = ['foo', 'bar', 'wow', 'okay']
const result = doTheMagic(one, two, three)
console.log(result)
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.15/lodash.min.js"></script>
Works with different length of arrays and makes use of functional programming for elegant code.
Here's a codepen to run: https://codepen.io/matteodem/pen/mddQrwe
Here's my Own solution, I was looking for any better idea
import { compact, flattenDeep } from "lodash/array";
export const doTheMagic = master => {
const longest = master.reduce((p, c, i, a) => (a[p].length > c.length ? p : i), 0);
const mixed = master[longest].map((i, k) => {
return master.map((o, a) => {
if (master[a][k]) return master[a][k];
});
});
const flaten = flattenDeep(mixed);
return compact(flaten);// to remove falsey values
};
const one = [1,2,3];
const two = ['a','b','c','d','e'];
const three = ['k','l','m','n'];
const mix = doTheMagic([one,two,three]);
console.log('mix: ', mix);
let a1 = [1, 2, 3, 4, 5];
let a2 = ["🏯", "🏜", "🏭", "🎢", "🌠", "🏗"];
let a3 = ['one', 'two', 'three', 'four', 'five'];
const doTheMagic = arrayOfArrays => {
let maxLength = 0;
let result = [];
for (arr in arrayOfArrays) {
maxLength = Math.max(maxLength, arrayOfArrays[arr].length);
}
for (let i = 0; i < maxLength; i++) {
for (let j = 0; j < arrayOfArrays.length; j++) {
if (arrayOfArrays[j][i]) {
result.push(arrayOfArrays[j][i]);
}
}
}
return result;
}
console.log(doTheMagic([a1, a2, a3]));
This works with an unknown number of arrays, each one of unknown length :
const arrays = [
[1, 2, 3, 4],
["a", "b", "c", "d", "e"],
["#", "#", "?"]
];
let output = [];
while (arrays.some(a => a.length)) { // While any of the arrays still has an element in it, keep going
for (let a of arrays) {
if (!a.length) continue;
output.push(a.shift()); // remove the first element of the array and add it to output
}
}
console.log(output)
This is my approach to achieve that, one for loop can make it. This will work if you don't know the number of arrays and array length as well.
function doTheMagic(arr){
let ans = [];
let maxLength = -1;
arr.forEach((tempArr)=>{
if(tempArr.length > maxLength){
maxLength = tempArr.length;
}
})
let l1=0,l2=0,l3=0;
for(let i=0;i<maxLength;i++){
arr.forEach((tempArr)=>{
if(tempArr[i]){
ans.push(tempArr[i]);
}
})
}
return ans;
}
let a1 = [1,2,3,4,5];
let a2 = ["🏯","🏜","🏭","🎢","🌠","🏗"];
let a3 = ['1','2','3','4','5'];
console.log(doTheMagic([a1,a2,a3]));
Not sure if this is the better, but how you can write code that handles any number of arrays passed in.
const weave = (...args) => // convert arguments to an array
args.reduce((res, arr, offset) => { // loop over the arrays
arr.forEach((v, i) => res[offset + i * args.length] = v) // loop over array and add items to their indexes
return res
}, []).filter(x => x !== undefined) // remove the unused indexes
const one = [1, 2, 3]
const two = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']
const three = ['w', 'x', 'y', 'z']
const result = weave(one, two, three)
console.log(result)
const result2 = weave(one, two)
console.log(result2)
const result3 = weave(one, two, three, ['*', '&'])
console.log(result3)

getting difference object from two objects using es6

Im trying to figure out whats the best way to get an intersection object between two objects using es6.
by this i mean something like:
a = {a:'a',b:'b',c:'c', d:'d'};
b = {a:'a',b: '1', c:'c', d:'2', f'!!!'}
// result I want:
c = getDifference(a,b)
//c is now: {b:'1', d:'2'}
Is there a short way to do this using es6, or do I need to iterate over the a object using for(in) with Object.keys() and compare, assigning intersections to c?
(a,b) => {
const c = {};
for(const _key in Object.keys(a)){
if(b[_key] && b[_key] !== a[_key]){
c[_key] = b[_key];
}
}
return c;
}
I know loadash/underscore has these kinds of helper functions... but trying to see if es6 has any new short syntax for this, and if not whats the shortest way to do this using vanilla js.
You can get the entries of object b using Object.entries() and then filter out the key-value pairs which are the same as those in a using .filter(), then, you can rebuild your object using Object.fromEntries() like so:
const a = {a:'a',b:'b',c:'c', d:'d'};
const b = {a:'a',b: '1', c:'c', d:'2', f:'!!!'}
const getDifference = (a, b) =>
Object.fromEntries(Object.entries(b).filter(([key, val]) => key in a && a[key] !== val));
// result I want:
const c = getDifference(a,b); // peforms b-a
console.log(c); // {b:'1', d:'2'}
If you can't support Object.fromEntries(), then you can use .reduce() instead to build the object for you:
const a = {a:'a',b:'b',c:'c', d:'d'};
const b = {a:'a',b: '1', c:'c', d:'2', f:'!!!'}
const getDifference = (a, b) =>
Object.entries(b).filter(([key, val]) => a[key] !== val && key in a).reduce((a, [key, v]) => ({...a, [key]: v}), {});
// result I want:
const c = getDifference(a,b); // peforms b-a
console.log(c); // {b:'1', d:'2'}
Use reduce for more concise code, but your approach was the clearest:
const getDifference = (a, b) => Object.entries(a).reduce((ac, [k, v]) => b[k] && b[k] !== v ? (ac[k] = b[k], ac) : ac, {});
We use Object.entries to avoid getting the value if we used Object.keys - it's just easier. Since b[k] may not exist, we use the short-circuit logical AND && operator - so if a key from a doesn't exist in b, it's not added to the result object. Then we check if the two values are equal - if they are, then nothing needs to be added, but if they're not, we add the key and value from b to the result object. In both cases, we return the result object.
You can achieve this in a single short with Object.keys();
const mainObj = {
a: 'a', b: 'b', c: 'c', d: 'd',
};
const comapareObj = {
a: 'a', b: '1', c: 'c', d: '2', f: '!!!',
};
const findVariantsElement = (main, compareWith) => {
const result = {};
Object.keys(main).forEach((r) => {
const element = main[r];
if (compareWith[r]) {
if (element !== compareWith[r]) {
result[r] = compareWith[r];
}
}
});
return result;
};
const c = findVariantsElement(mainObj, comapareObj);
console.log(c);
You can use .map() also for the same
const mainObj = {
a: 'a', b: 'b', c: 'c', d: 'd',
};
const comapareObj = {
a: 'a', b: '1', c: 'c', d: '2', f: '!!!',
};
const findVariantsElement = (main, compareWith) => {
const result = {};
Object.keys(main).map((r) => {
const element = main[r];
if (compareWith[r]) {
if (element !== compareWith[r]) {
result[r] = compareWith[r];
}
}
});
return result;
};
const c = findVariantsElement(mainObj, comapareObj);
console.log(c);

Merge two arrays which inlclude null and object element?

If I have two javascript arrays.
const a = [null,null,{a:1}];
const b = [{c:3},null,{a:3,b:2}];
I want a function which can return the following result .
[{c:3},null,{a:3,b:2}]
And the above still can apply to the following.
const a = [];
const b = [null,null,{t:4}];
I want to have the following result.
[null,null,{t:4}]
Can someone help me? Thanks !
It looks like you want to merge those objects that have the same index in the input arrays:
function merge(a, b) {
const merged = [];
for (let i = 0; i < a.length || i < b.length; i++) {
merged.push(a[i] && b[i] ? {...a[i], ...b[i]} : a[i] || b[i]);
}
return merged;
}
console.log(merge([null,null,{a:1}], [{c:3},null,{a:3,b:2}]));
console.log(merge([], [null,null,{t:4}]));
You could merge the items by checking the falsy value as well.
const
merge = (...a) => a.reduce(
(r, a) => (a.forEach((o, i) => r[i] = o || r[i] || !r[i] && o), r),
[]
);
console.log(merge([null, null, { a: 1 }], [{ c: 3 }, null, { a: 3, b: 2 }]));
console.log(merge([], [null, null, { t: 4 }]));
.as-console-wrapper { max-height: 100% !important; top: 0; }
Concat or Spread Operator should do it..
const a1 = [];
const b1 = [null,null,{t:4}];
const result1 = [...a1, ...b1];
console.log(result1);
const a2 = [null,null,{a:1}];
const b2 = [{c:3},null,{a:3,b:2}];
const result2 = [...a2, ...b2];
console.log(result2);
const a = [];
const b = [null,null,{t:4}];
const c = [...a, ...b];
console.log(c);
How about using the spread operator?
You can also use concat method to merge two arrays like so
const a = [];
const b = [null,null,{t:4}];
const c = a.concat(b);
console.log(c)
I like to split the processing into two parts:
const zipWith = (fn) => (a, b) => [...Array(Math.max(a.length, b.length))]
.map((_, i) => fn(a[i], b[i]))
const combine = zipWith ((a, b) =>
a && b ? {...a, ...b} : a ? {...a} : b ? {...b} : null)
console.log(combine([null, null, {a: 1}], [{c: 3}, null, {a: 3, b: 2}]))
console.log(combine([], [null, null, {t: 4}]))
.as-console-wrapper { max-height: 100% !important; top: 0; }
"zip" is a common name for a function which combines two arrays, index-by-index. Sometimes "zipWith" is used for an extension which takes a function for deciding how to combine those two values. This version of zipWith is slightly more complicated than other variations, as it uses the longer length of its two input arrays, rather than just the length of the first one (which might be written zip = (fn) => (a1, a2) => a1.map((a, i) => fn(a, a2[i])).)
combine calls this with a function that handles the four cases : a null / b null, a null / b non-null, a non-null / b null, and a non-null / b non-null, using or combining the values supplied.
zipWith is a quite reusable function that you might find helpful elsewhere in your app. combine is more specific to this case.

Categories