immutablejs mergeWith objects but overwrite new lists - immutable.js

I'm merging objects together, but one issue I'm having is that when I merge an object with a list, instead of using one of the other, it combines them. Is there anyway to get around this?
Ie.
obj1 = fromJS({name: 'kim', friends: [1,2,3]})
obj2 = fromJS({pet: 'Alex', friends: [4,5,6]})
obj1.mergeWith(obj2)
===> Desired Result
obj3 = fromJS({name: 'kim', pet: 'Alex', friends: [4,5,6]})
===> Actual Result
obj3 = fromJS({name: 'kim', pet: 'Alex', friends: [1,2,3,4,5,6]})

immutable gives you a mergeWith function which lets you define how to deal with conflicts.
export function mergeWithoutList(prev, next){
if(typeof next == 'object'){
if(List.isList(prev)){
return next
}else{
return prev.mergeWith(mergeWithoutList, next)
}
}
return next
}
obj3 = obj1.mergeWith(mergeWithoutList, obj2)
=>
obj3 == fromJS({name: 'kim', pet: 'Alex', friends: [4,5,6]})

Related

Make a Person Using Getters and Setters: JavaScript not understanding where my codebase is performing the wrong action. Help requested

The "Make a Person" intermediate algorithm scripting challenge on freeCodeCamp requires you to fill an object constructor with the following methods:
/*
getFirstName()
getLastName()
getFullName()
setFirstName(first)
setLastName(last)
setFullName(firstAndLast)
*/
My code is as follows and includes the test cases and their required values commented in at the end:
const Person = function(firstAndLast) {
// Only change code below this line
// Complete the method below and implement the others similarly
//create a holder variable to hold a copy of the full name passed as parameter
let fullName = firstAndLast;
//create a variable to pay respect to "DRY" so as not to have to type this variable and method multiple times throughout
let splitter = fullName.split(" ");
//return the first name from the full name passed as parameter
this.getFirstName = function() {
return splitter[0];
};
//return the last name from the full name passed as parameter
this.getLastName = function() {
return splitter[1];
};
//return the full name passed as a parameter
this.getFullName = function() {
return fullName;
};
//update the full name to now include the given first name instead of the original passed parameter
this.setFirstName = function(first) {
fullName = first + " " + splitter[1];
};
//update the full name to now include the given last name instead of the original passed parameter
this.setLastName = function(last) {
fullName = splitter[0] + " " + last;
};
//update the full name to the given firstAndLast name instead of the original passed parameter
this.setFullName = function(newFull) {
fullName = newFull;
};
};
//create a new Person, bob, and name him 'Bob Ross'
const bob = new Person('Bob Ross');
//expected to return => 'Bob Ross'
let result = bob.getFullName();
//no expected return value, but fullName should now return => 'Haskell Curry'
bob.setFullName('Haskell Curry')
//my code here returns => 'Haskell Curry'
let result2 = bob.getFullName();
//my code here returns => 'Bob'
//should be returning => 'Haskell'
let result3 = bob.getFirstName();
//my code here returns => 'Ross'
//should be returning => 'Curry'
let result4 = bob.getLastName();
//Console.log in place for value testing during algorithm creation
console.log(result, result2, result3, result4)
//Check for length of bob, should not exceed 6 for the purposes of this test
console.log(Object.keys(bob).length)
/*Tests
Required returning values for each test
bob instanceof Person => true (Passing)
Object.keys(bob).length => 6 (Passing)
bob.firstName => undefined (Passing)
bob.lastName => undefined (Passing)
bob.getFirstName() => "Bob" (Passing)
bob.getLastName() => "Ross" (Passing)
bob.getFullName() => "Bob Ross" (Passing)
bob.getFullName() => "Haskell Ross" AFTER bob.setFirstName("Haskell") (Passing)
bob.getFullName() => "Haskell Curry" AFTER bob.setLastName("Curry") (Passing)
bob.getFullName() => "Haskell Curry" AFTER bob.setFullName("Haskell Curry") (Passing)
bob.getFirstName() => "Haskell" AFTER bob.setFullName("Haskell Curry") (NOT Passing)
bob.getLastName() => "Curry" AFTER bob.setFullName("Haskell Curry") (NOT Passing)
*/
After checking my code up against the solution code, the two are virtually the same, the only differences are the usage of
let splitter = fullName.split(" ")
//this does not exist in the solution code
//used in my code to avoid having to type fullName.split(" ") multiple times throughout
And where the setters ask for parameters "first", "last", and "newFull", respectively, the solution code uses "name" for each instead
I couldn't imagine that these two differences could make that big of a difference, so could I get some clarity in the understanding of their importance, and furthermore, why my code won't pass all the cases as is? Thanks in advance!

Compare two JSON objects in Angular

I want to check if two JSON objects are same in Typescript(Angular), ignoring some key value pairs which may be optional.
obj1 = {name: 'Anna', password: 'test123', status: 'active'}
obj2 = {name: 'Anna', password: 'test123'}
Technically 'status' is optional, so I want the comparison to return true only considering first two properties. How to check this?
You could:
Get common keys of obj1 and obj2 (with a set)
Compare each key one-to-one
function compare(obj1, obj2) {
const commonKeys = [...new Set([...Object.keys(obj1), ...Object.keys(obj2)])];
for (const key of commonKeys) {
if (obj1[key] !== obj2[key]) {
return false;
}
}
return true;
}
try this
Boolean theSame= obj1.name==obj2.name && obj1.password==obj2.password;

conditionally copy an object using ES6

Lets say I have the following two objects
object1: {
CustomerId: 1,
CustomerData: [1,2,3,4]
}
object2: {
CustomerId: 2,
CustomerData: [11,22,33,44]
CustomerOrders: [5,6,7,8]
}
Now I need to merge the two objects using the following business logic
If either object contains a CustomerId then that should override the properties of the other object. For example if the two objects were
object1: {
CustomerId: 1,
CustomerData: [1,2,3,4]
}
object2: {
CustomerData: [11,22,33,44]
CustomerOrders: [5,6,7,8]
}
Since object2 does not contain CustomerId the final merged object should be as follows
mergedObject: {
CustomerId: 1,
CustomerData: [1,2,3,4],
CustomerOrders: [5,6,7,8]
}
Note* I have preserved the CustomerData of object1 and not object2 since object2 lacks customerId.
I am looking to use the ES6 spread operator to achieve this. So I will use something like this
const mergedObject = {...val1, ...val2}
My problem is that what comes first in the {} is determined as to which object contains the customerId. There at the moment I have an if condition which checks if customerId is found in the object and then assign to object2 since that will override the other. so I have something like this
if (object1.customerId){
val2 = object1;
val1 = object2;
} else if (object2.customerId){
val2 = object2;
val1 = object1;
}
This is very long and in my opinion ugly, is there a better way I can do this in the spread/constructor operator itself?
const mergedObject = {...val1, ...val2} //Do it here itself
let mergedObject;
if (object1.customerId) {
// Consider that even if both have customerId object1 override object2
// You might want to check if (object1.customerId && !object2.customerId)
mergedObject = {...object2, ...object1}
}
else {
// Event if both have customerId object 2 will override object 1
mergedObject = {...object1, ...object2}
}
// or with Ternary:
let mergedObject = object1.customerId ? {...object2, ...object1} : {...object1, ...object2}

Using Object.assign and object spread more efficiently

I am trying to achieve the following:
Say i have an object like this:
myObj = {
john: {1: ['a', 'b',..], 2: ['aa', 'vv',...],
tom: {1: ['ab', 'bb',..], 2: ['aa', 'vv',...],
}
To achieve the above i am doing something like this which works
function (name, myNum, myList) {
let myObj = Object.assign({}, state);
// name and myNum and myList values are passed in for this eg i am
// hardcoding
let name = 'jon';
let myNum = 1;
let mylist = [1,2,3];
// I want to replace the if / else with a more elegant solution
if (myObj.hasOwnProperty(name)) {
myObj[name][myNum] = myList;
} else {
myObj[name] = {[myNum]: myList};
}
return myObj;
}
I am sure there is a much cleaner way to do this, using Object.assign or object spread.
Please advice what would be better approach.
Maybe you are looking for
myObj[name] = Object.assign(myObj[name] || {}, {[myNum]: myList});
You can also do
myObj[name] = {...myObj[name], [myNum]: myList};
but this will always create a new object.
You can also combine everything into a single expression:
function (name, myNum, myList) {
return {
...state,
[name]: {
...state[name],
[myNum]: myList,
},
};
}

Adding elements to Immutable List within forEach

I have an Immutable Map, where each key is an arbitrary category (these will be a List). I then have an array of strings that each category must have. If a specific category does not contain that string, it will be added to its respective List inside the Map.
const categories = new Map({
cat1: new List(['animal', 'color']),
cat2: new List(['animal']),
});
const missingKeys = new Map({
cat1: new List(),
cat2: new List(),
});
categories.keySeq().toArray().forEach((category) => {
const keys = categories.get(category).keySeq().toArray();
const requiredKeys = ['animal', 'color'];
// loop through required keys and push any key that is not found
// in the categories Map
requiredKeys.forEach((key) => {
if (keys.indexOf(key) === -1) {
// add missing keys to respective category of `missingKeys`
missingKeys.get(category).push(key)
}
});
});
I expect that after the loop completes, categories will be updated accordingly. However, when I try to console.log categories, the Map has not been updated.
You seem to be missing the whole "immutable" part of this library. The line
missingKeys.get(category).push(key)
does not mutate missingKeys or any of the lists it contains. It returns a new List with the missing key at the end.
If you want missingKeys to change, you need to reassign the variable:
missingKeys = missingKeys.update(category, keyList => keyList.push(key));
Don't forget to change that
As a side note, you're doing a lot of weird conversion with keySeq and toArray. What you have could be written more simply as:
const requiredKeys = ['animal', 'color'];
categories.forEach((keys, category) => {
requiredKeys.forEach(requiredKey => {
if (!keys.contains(key)) {
missingKeys = missingKeys.update(category, keyList => keyList.push(key));
}
});
});
Or the even more idiomatic functional way:
const requiredKeys = List(['animal', 'color']);
const missingKeys = categories.map(keys =>
requiredKeys.filter(key =>
!keys.contains(key)))