Ramda does not provide deep mixin? - javascript

var _ = require('ramda');
var obj1 = {
innerNum: 1,
innerObj: {
innerStr: 'a',
innerStrB: 'Z'
}
};
var obj2 = {
innerNum: 2,
innerObj: {
innerStr: 'b'
}
};
var mixedObj = _.mixin(obj1, obj2);
mixedIObj does not include the inner object's innerStrB.
Any ramda solution?

It's not clear what you would want here. Most mixin/extend implementations I've seen are shallow, adding the values of the keys in the second object to those of the first, overriding where the keys are duplicated.
Ramda does have functions to clone an object with an updated value at a particular path: assocPath. I'm guessing that this wouldn't do everything you seem to want, though:
R.assocPath('innerObj.innerStr', 'b', obj1); //=>
// {
// innerNum: 1,
// innerObj: {
// innerStr: 'b',
// innerStrB: 'Z'
// }
// }
The question is what a deep mixin would really mean. If the objects have the same structure, it's pretty clear, but if they don't, it might be a little hairy:
mixinDeep(
{a: 1, b: {c: 2, d: 3}},
{b: {d: {e: 4, f: 5}, g: 6}}
);
// ?=>? {a: 1, b: {c: 2, d: {e: 4, f: 5}, g: 6}}
If that's the sort of thing you're looking for, then the Ramda team would love to have an issue raised or even a pull request.

Related

Jest failing one function but not another

I am writing some daily challenges for my coding bootcamp and I am running into an issue on one problem. I wrote a function that combines objects and it works correctly. Here is what the problem prompt is
Prompt: Write a function named mergeObjects that accepts at least two objects as arguments, merges the properties of the second through n objects into the first object, then finally returns the first object. If any objects have the same property key, values from the object(s) later in the arguments list should overwrite earlier values.
Examples:
mergeObjects({}, {a: 1}); //=> {a: 1} (same object as first arg)
mergeObjects({a: 1, b: 2, c: 3}, {d: 4}); //=> {a: 1, b: 2, c: 3, d: 4}
mergeObjects({a: 1, b: 2, c: 3}, {d: 4}, {b: 22, d: 44}); //=> {a: 1, b: 22, c: 3, d: 44
My function
function mergeObjects(...obj) {
let obj1 = {}
obj.forEach(element => {
obj1 = {...obj1, ...element}
});
return obj1
}
Solution function
function mergeObjects1(target, ...objects) {
objects.forEach(function (obj) {
// using ES2015's 'for in' loop
for (var key in obj) {
target[key] = obj[key]
}
})
return target
}
In my eyes these two functions provide the same result. However, when I run my code through the jest test they created it fails on the first test. But the solution they provide, does not fail. The jest test is below.
describe('15-mergeObjects', function () {
it('returns same object', function () {
var obj = {}
expect(mergeObjects(obj, { a: 1 })).toBe(obj)
})
it('adds additional properties', function () {
expect(mergeObjects({ a: 1, b: 2, c: 3 }, { d: 4 })).toEqual({
a: 1,
b: 2,
c: 3,
d: 4
})
})
it('merges props from left to right', function () {
expect(
mergeObjects({ a: 1, b: 2, c: 3 }, { d: 4 }, { b: 22, d: 44 })
).toEqual({ a: 1, b: 22, c: 3, d: 44 })
})
})
describe('15-mergeObjects', function () {
it('returns same object', function () {
var obj = {}
expect(mergeObjects1(obj, { a: 1 })).toBe(obj)
})
it('adds additional properties', function () {
expect(mergeObjects1({ a: 1, b: 2, c: 3 }, { d: 4 })).toEqual({
a: 1,
b: 2,
c: 3,
d: 4
})
})
it('merges props from left to right', function () {
expect(
mergeObjects1({ a: 1, b: 2, c: 3 }, { d: 4 }, { b: 22, d: 44 })
).toEqual({ a: 1, b: 22, c: 3, d: 44 })
})
})
Can anyone provide an explanation as to why the solution function passes while my function does not?
While the results look the same, the two functions are slightly different.
In your function, you are creating a new object and then adding to it all of the required properties.
In the solution function, they are adding properties to the original target object and then returning it.
The returned objects from both functions will have the same keys and the same values, but different references, so they are not considered the same object in JavaScript.
In the test
expect(mergeObjects1(obj, { a: 1 })).toBe(obj)
.toBe() checks whether two objects are the same (identity), therefore it fails the case of your function.
Note that there is a different test, .toEqual(), which checks whether two objects have the same keys and the same values (but possibly different references). Your function would pass this test
expect(mergeObjects1(obj, { a: 1 })).toEqual(obj)

How to convert an array containing arrays of objects to single array witht hose object using Ramda expressions?

I am having a difficult time, there is some bad mapping going on on my code.
I have an array containing array of objects like that :
[
[{a: 1, b: 2},{a: 1, b: 3} ],
[{a: 5, b: 2},{a: 2, b: 5}]
]
And I want to make like that :
[
{a: 1, b: 2},
{a: 1, b: 3},
{a: 5, b: 2},
{a: 2, b: 5}
]
In order to do that, I thought I found the magical solution, make things flat, using flatten function, it was not working ( this problem is just a piece of code in a lot of code ) and I was wondering why, i wasted some time to find that this the problem, it is not the behovior I am expecting, as you can see in the image, the first thing I have is an array containing an array having two objects, with flatten method, I was expecting an array of two objects, but I am getting what you see in the image :
The code I have ttried is this :
const expectedArray = R.flatten(myArrayOfArraysOfObjects);
Full example :
const singleTronconPoints = troncon => {
return troncon.geometri_linestring;
};
console.log('troncons : ');
console.log(troncons);
console.log('map troncons points');
console.log(map(singleTronconPoints, troncons));
console.log('flatten');
console.log(flatten(map(singleTronconPoints, troncons)));
and this is full result :
How can I solve that, is there another magical ( :P ) solution ( method ) to solve the problem ?
Any help would be much appreciated.
Array.prototype.reduce() can also be an option:
const arr =[
[{a: 1, b: 2},{a: 1, b: 3}],
[{a: 5, b: 2},{a: 2, b: 5}]
]
const expectedArray = arr.reduce((acc, array) => {
acc.push(...array);
return acc;
}, []);
You can use array.flat
let a = [
[{
a: 1,
b: 2
}, {
a: 1,
b: 3
}],
[{
a: 5,
b: 2
}, {
a: 2,
b: 5
}]
];
let b = a.flat();
console.log(b)
Alternatively you can use reduce and inside callback use forEach and puch items from the nested array to accumulator array
let a = [
[{
a: 1,
b: 2
}, {
a: 1,
b: 3
}],
[{
a: 5,
b: 2
}, {
a: 2,
b: 5
}]
];
let b = a.reduce((acc, curr) => {
curr.forEach(item => acc.push(item))
return acc;
}, []);
console.log(b)
use reduce() + push which faster flat() method.
refer this for to check performance. : https://jsbench.me/0ikcqa83ck/1
let arr = [
[{a: 1, b: 2},{a: 1, b: 3} ],
[{a: 5, b: 2},{a: 2, b: 5}]
]
console.log(arr.flat())
let flattenArr = arr.reduce((acc, val) => (acc.push(...val),acc), [])
console.log(flattenArr);
Array.flat is the magical solution you are looking for !
var arr = [
[{a: 1, b: 2},{a: 1, b: 3} ],
[{a: 5, b: 2},{a: 2, b: 5}]
]
console.log(arr.flat())

How to prototype on object instance that is not Object or Function?

Extending Object class is not recommended, so I try to extend an object, for example:
var obj = {'a': 1, 'b': 2, 'c': 3};
Here the object literal {'a': 1, 'b': 2, 'c': 3}
is same as new Object({'a': 1, 'b': 2, 'c': 3}).
I tried obj.prototype = {d: 4} but it set 'prototype' as property, not a real prototype.
Forget Object.defineProperties!
Also tried Object.create: Why does this not work? ...
var ext = Object.create(obj, {'d': { value:4 }});
console.log(obj.isPrototypeOf(ext)) // => true! obj is prototype of ext
console.log(ext); // => {d: 4}
console.log(obj); // => {a: 1, b: 2, c: 3}
Console.log say obj.isPrototypeOf(ext) == true so why is ext not {'a': 1, 'b': 2, 'c': 3, 'd': 4} ?
How to prototype on object instance that is not Object class or Function?
Update: As Nicholas Tower answer i have missed enumerable in the second parameter that should be: {'d': { value:4, enumerable: true }}
I had issue with arrow-dropdown in Chrome console that I missed to click to see the inherited values. I could have used assign() to "extend" obj. Now it show obj = {'a': 1, 'b': 2, 'c': 3} __proto__ {d: 4} that is ok. From OO view that means obj extends {d: 4}*. Made a prototype on the object obj.
I accepted answer from t.888 that helped me see how console.log show objects and the correct way to extend an existing object.
why is ext not {'a': 1, 'b': 2, 'c': 3, 'd': 4} ?
It is, but you didn't make that d enumerable, so console.log doesn't see it.
const obj = {'a': 1, 'b': 2, 'c': 3};
const ext = Object.create(obj, {'d': {
value:4,
enumerable: true // <---- added this
}});
console.log('ext', ext);
for (const key in ext) {
if (ext.hasOwnProperty(key)) {
console.log('own property', key, ext[key]);
} else {
console.log('inherited property', key, ext[key]);
}
}
You can use __proto__ to set a prototype but its considered a legacy feature:
const obj = {a: 1, b: 2, __proto__: { c: 3 }}
console.log(obj.c) // 3
A better way is to extend an existing object with Object.create. It's convenient to define a function
for this purpose:
/**
* Extend an object by creating a new object with the given prototype.
*/
function extend(proto, obj) {
// Create a new object with the given prototype
const sub = Object.create(proto)
if (obj) {
// Copy the properties from 'obj' to the new object.
Object.assign(sub, obj)
}
// Return the new object.
return sub
}
// Define an object to serve as a prototype.
const proto = { a: 1, b: 2 }
// Create a new object with the given prototype.
const extended = extend(proto, { c: 3 })
console.log(extended.a, extended.b, extended.c) // 1 2 3
However as another answer pointed out, it won't show the prototype properties actually
on the object:
console.log(extended) // { c: 3 }
It's there though, it's just not on the object itself but on its prototype:
for (let key in extended) {
console.log(key, extended[key])
}
Output:
c 3
a 1
b 2
console.log(extended.__proto__) // { a: 1, b: 2 }
Object.assign
If you just want to copy and/or combine objects, use Object.assign:
const first = {a: 1}
const second = {b: 2}
// Put the properties from 'second' onto 'first'.
Object.assign(first, second) // first: {a: 1, b: 2}
// Copy 'first' and 'second' to a new object.
const third = Object.assign({c: 3}, first, second) // third: {c: 3, a: 1, b: 2}
This is roughly equivalent to copying properties manually:
const first = {a: 1}
const second = {b: 2}
for (let key in second) {
if (second.hasOwnProperty(key)) {
first[key] = second[key]
}
}
console.log(first) // {a: 1, b: 2}
Generalizing that code, we can create an approximation of Object.assign that works
on older browsers where Object.assign may not present:
/**
* Assign properties from object arguments [1..n] to the
* zeroth object argument.
*/
function assign() {
var first, rest
// Check of Object.assign exists and use a fallback if it doesn't.
if (typeof Object.assign === 'function') {
// Copy each object's properties to the first one.
return Object.assign.apply(null, arguments)
} else {
first = arguments[0]
rest = Array.prototype.slice.call(arguments, 1)
// Copy each object's properties to the first one.
rest.forEach((obj) => {
for (var key in obj) {
// Don't copy any of obj's prototype's properties.
if (obj.hasOwnProperty(key)) {
first[key] = obj[key]
}
}
})
return first
}
}
const obj = assign({c: 3}, {a: 1}, {b: 2})
console.log(obj) // {c: 3, a: 1, b: 2}

What's the fastest way to remove an object from an array that has a specific property?

How can I remove:
var arr = [{a: 1, b: 2}, {a: 3, b: 4}, {a: 5, b: 6}, {a: 7, b: 8}];
the object in the array with b set to 2?
arr = arr.filter(function( a ) {
return a.b !== 2;
});
This will only work for IE9+
I think the fastest way to do that would be a for loop, correct me if I'm wrong...
var arr = [{a: 1, b: 2}, {a: 3, b: 4}, {a: 5, b: 6}, {a: 7, b: 8}];
for(var i=0; i<arr.length; i++) {
if(arr[i].b==2) { arr.splice(i,1); }
}
According to jsperf the fastest way to delete an array element is to use delete array[index]. Source: http://jsperf.com/object-delete-vs-array-splice-vs-array-delete
A solution could be this: http://jsfiddle.net/no20bv5o/3/
But because of the overhead in the for loop and the second array it is slower as the before mentioned arr.filter() as shown by this jsperf: http://jsperf.com/filter-array-by-element-attribute

Node not logging deeper subobject

This may have been answered, but I did search.
In js file:
console.log({
a: 1,
b: { c: 1},
d: [{e:1},{f:1}],
g: [{h:[1,2,3]}]
});
This is what actually prints:
{ a: 1,
b: { c: 1 },
d: [ { e: 1 }, { f: 1 } ],
g: [ { h: [Object] } ]
}
Notice 'h' value, can I print this?
Read the docs for util.inspect()
It allows you to specify the depth to print at:
The default is to only recurse twice. To make it recurse indefinitely, pass in null for depth.
To use it, you could call console.log(util.inspect(yourObj, true, null));
console.dir() says it uses util.inspect(), but doesn't show parameters to modify the inspect() call, so it would probably only recurse twice, too.
You can use for loop to iterate over it..
var obj = {
a: 1,
b: {c: 1},
d: [{e: 1}, { f: 1}],
g: [{ h: [1, 2, 3]}]
};
var data = obj.g[0].h ;
for(var i =0; i <data.length ; i++){
console.log(data[i])
}​
Check Fiddle
I have used JSON.stringify() to achieve it, you can use parameters to determine the formatting.
As written above, util.inspect() also works.

Categories

Resources