Adding elements to Immutable List within forEach - immutable.js

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)))

Related

Write key and value to JSON file based on the data provided using Cypress

I have to write the captured data from the application in JSON file as like below:
let expectedKey = 'PaperCode';
cy.get('app-screen').find('#code-details').invoke('val').as(code);
cy.get('#code').then(code) => {
cy.readFile('cypress/fixtures/applicationDetails.json').then((appDetails) => {
if(expectedKey === 'StudentCode'){
appDetails.StudentCode = code;
}
if(expectedKey === 'DepartmentCode'){
appDetails.DepartmentCode = code;
}
if(expectedKey === 'PaperCode'){
appDetails.PaperCode = code;
}
if(expectedKey === 'ResultsCode'){
appDetails.ResultsCode = code;
}
})
})
Here, the key and its value are added to json in multiple if blocks. Still, there are many if blocks to implement based on different codes. I want to remove the if blocks and need to add the key and its value to json file based on the expectedKey. Any help please?
Using bracket notation appDetails[expectedKey] to define the new property,
let expectedKey = 'studentCode';
cy.readFile('cypress/fixtures/applicationDetails.json').then((appDetails) => {
cy.get('app-screen').find('#code-details').invoke('val')
.then(code) => {
appDetails[expectedKey] = code; // add (or overwrite) new key
cy.writeFile('cypress/fixtures/applicationDetails.json', appDetails)
})
})
Clearing the file of all keys except the one you want
const expectedKey = 'studentCode';
const appDetails = {}
cy.get('app-screen').find('#code-details').invoke('val')
.then(code) => {
appDetails[expectedKey] = code; // add (or overwrite) new key
cy.writeFile('cypress/fixtures/applicationDetails.json', appDetails)
})

How to alter keys in immutable map?

I've a data structure like this (generated by normalizr):
const data = fromJS({
templates: {
"83E51B08-5F55-4FA2-A2A0-99744AE7AAD3":
{"uuid": "83E51B08-5F55-4FA2-A2A0-99744AE7AAD3", test: "bla"},
"F16FB07B-EF7C-440C-9C21-F331FCA93439":
{"uuid": "F16FB07B-EF7C-440C-9C21-F331FCA93439", test: "bla"}
}
})
Now I try to figure out how to replace the UUIDs in both the key and the value of the template entries. Basically how can I archive the following output:
const data = fromJS({
templates: {
"DBB0B4B0-565A-4066-88D3-3284803E0FD2":
{"uuid": "DBB0B4B0-565A-4066-88D3-3284803E0FD2", test: "bla"},
"D44FA349-048E-4006-A545-DBF49B1FA5AF":
{"uuid": "D44FA349-048E-4006-A545-DBF49B1FA5AF", test: "bla"}
}
})
A good candidate seems to me the .mapEntries() method, but I'm struggling on how to use it ...
// this don't work ... :-(
const result = data.mapEntries((k, v) => {
const newUUID = uuid.v4()
return (newUUID, v.set('uuid', newUUID))
})
Maybe someone can give me a hand here?
mapEntries is the correct method. From the documentation, the mapping function has the following signature:
mapper: (entry: [K, V], index: number, iter: this) => [KM, VM]
This means that the first argument is the entry passed in as an array of [key, value]. Similarly, the return value of the mapper function should be an array of the new key and the new value. So your mapper function needs to look like this:
([k, v]) => {
const newUUID = uuid.v4()
return [newUUID, v.set('uuid', newUUID)]
}
This is equivalent to the following (more explicit) function:
(entry) => {
const key = entry[0]; // note that key isn't actually used, so this isn't necessary
const value = entry[1];
const newUUID = uuid.v4()
return [newUUID, value.set('uuid', newUUID)]
}
One thing to note is that the templates are nested under the templates property, so you can't map data directly -- instead you'll want to use the update function.
data.update('templates', templates => template.mapEntries(...)))
So putting everything together, your solution should look like the following:
const result = data.update('templates', templates =>
templates.mapEntries(([k, v]) => {
const newUUID = uuid.v4()
return [newUUID, v.set('uuid', newUUID)]
})
);

Decypher ES6 const destructuring declaration

Can someone help me decypher this ES6 statement?
const {
isFetching,
lastUpdated,
items: posts
} = postsByReddit[selectedReddit] || {
isFetching: true,
items: []
}
I pulled it from the Redux async example - https://github.com/reactjs/redux/blob/master/examples/async/containers/App.js#L81
The code is simply declaring three constants, getting them from similarly named properties on an object if it is non-empty, otherwise get them from an object literal that acts as default values.
I trust that you are confused over the object like syntax rather than the const keyword.
var|let|const { ... } = ... is an object destructuring declaration.
var|let|const [ ... ] = ... is an array destructuring declaration.
Both are short hand for "break down right hand side and assign to left hand side".
Destructuring can be done on array or object using different brackets.
It can be part of a declaration or as stand-alone assignment.
const { isFetching } = obj; // Same as const isFetching = obj.isFetching
var [ a, b ] = ary; // Same as var a = ary[0], b = ary[1]
[ a ] = [ 1 ]; // Same as a = 1
For object destructuring, you can specify the property name.
For array, you can skip elements by leaving blank commas.
Destructuring can also form a hierarchy and be mixed.
const { items: posts } = obj; // Same as const posts = obj.items
var [ , , c ] = ary; // Same as var c = ary[2]
let { foo: [ { bar } ], bas } = obj; // Same as let bar = obj.foo[0].bar, bas = obj.bas
When destructuring null or undefined, or array destructure on non-iterable, it will throw TypeError.
Otherwise, if a matching part cannot be found, its value is undefined, unless a default is set.
let { err1 } = null; // TypeError
let [ err3 ] = {}; // TypeError
let [ { err2 } ] = [ undefined ]; // TypeError
let [ no ] = []; // undefined
let { body } = {}; // undefined
let { here = this } = {}; // here === this
let { valueOf } = 0; // Surprise! valueOf === Number.prototype.valueOf
Array destructuring works on any "iterable" objects, such as Map, Set, or NodeList.
Of course, these iterable objects can also be destructed as objects.
const doc = document;
let [ a0, a1, a2 ] = doc.querySelectorAll( 'a' ); // Get first three <a> into a0, a1, a2
let { 0: a, length } = doc.querySelectorAll( 'a' ); // Get first <a> and number of <a>
Finally, don't forget that destructuring can be used in any declarations, not just in function body:
function log ({ method = 'log', message }) {
console[ method ]( message );
}
log({ method: "info", message: "This calls console.info" });
log({ message: "This defaults to console.log" });
for ( let i = 0, list = frames, { length } = frames ; i < length ; i++ ) {
console.log( list[ i ] ); // Log each frame
}
Note that because destructuring depends on left hand side to specify how to destructre right hand side,
you cannot use destructring to assign to object properties.
This also excludes the usage of calculated property name in destructuring.
As you have seen, destructuring is a simple shorthand concept that will help you do more with less code.
It is well supported in Chrome, Edge, Firefox, Node.js, and Safari,
so you can start learn and use it now!
For EcmaScript5 (IE11) compatibility, Babel and Traceur transpilers
can turn most ES6/ES7 code into ES5, including destructuring.
If still unclear, feel free to come to StackOverflow JavaScript chatroom.
As the second most popular room on SO, experts are available 24/7 :)
This is an additional response to the already given. Destructuring also supports default values, which enables us to simplify the code:
const {
isFetching = true,
lastUpdated,
items = []
} = postsByReddit[selectedReddit] || {};
Basically:
var isFecthing;
var lastUpdated;
var posts;
if (postsByReddit[selectedReddit]) {
isFecthing = postsByReddit[selectedReddit].isFecthing;
lastUpdated = postsByReddit[selectedReddit].lastUpdated;
posts = postsByReddit[selectedReddit].items.posts;
} else {
isFecthing = true;
items = [];
}

In ImmutableJS, how to push a new array into a Map?

How can I achieve the following using ImmutableJS:
myMap.get(key).push(newData);
You can do as follows: (see this JSBin)
const myMap = Immutable.fromJS({
nested: {
someKey: ['hello', 'world'],
},
});
const myNewMap = myMap.updateIn(['nested', 'someKey'], arr => arr.push('bye'));
console.log(myNewMap.toJS());
// {
// nested: {
// someKey: ["hello", "world", "bye"]
// }
// }
Since myMap is immutable, whenever you try to set/update/delete some data within it, it will return a reference to the new data. So, you would have to set it to a variable in order to access it (in this case, myNewMap).
If the array referenced at the key is a plain javascript array - then you will actually mutate that value - so your code will work as expected (ie - myMap will contain a mutable/mutated array at 'key' with the newData pushed in.) However, this kind of defeats the purpose of immutability so I would recommend that the key in myMap reference an Immutable.List. In which case you'll want to do:
var newMap = myMap.set('key', myMap.get('key').push(newData))

AngularJS - Create dynamic properties to an object/model from json

OK, we all know this works:
vm.myObject = {
required : "This field requires data",
.....
}
But how can I create that same object dynamically when the property 'keys' and 'values' come from a json file, eg:
json:
[
{ "key" :"required", "value": "This field requires data"},
.....
]
service:
var myObject = {}
DynamicObjSvc.get()
.success(function(data){
data.forEach(function(item){
// pass each key as an object property
// and pass its respective value
?????????
})
.....
UPDATE:
Kavemen was mostly correct, this turned out to be the solution:
var myObject = {};
DynamicObjSvc.all()
.success(function(data){
angular.forEach(data, function(msg) {
myObject[msg.key] = msg.value; <-- his answer was incorrect here
});
$fgConfigProviderRef.validation.message(myObject);
})
.error(function(err){
console.log(err.message);
})
You can use angular.forEach and the bracket notation for setting (and getting) object properties in Javascript
var myObject = {}
DynamicObjSvc.get().success(
function(data) {
angular.forEach(data, function(value, key) {
myObject[key] = value;
});
}
);
See also Working with Objects from MDN
EDIT
I see now that your data is really an array of objects, not just a single object, so yes, the code above could lead you astray.
In any case, the method of setting an object's properties dynamically using the bracket notation is sound; the loop could be reworked to handle your data array as such:
//we have an array of objects now
var myObjects = [];
DynamicObjSvc.get().success(
function(data) {
//for each object in the data array
for(var i = 0; i < data.length; i++) {
//create and populate a new object for the ith data element
var newObject = {};
angular.forEach(data[i], function(value, key) {
newObject[key] = value;
});
//and add it to the overall collection
myObjects.push(newObject);
}
}
);