Immutable.js reference equality - immutable.js

given the following trivial code:
const Immutable = require('immutable');
const a = Immutable.fromJS({
a: 1,
b: [2, 3, 4],
c: {
d: 1
}
});
const b = a.setIn(['c', 'd'], "Something else");
const c = b.setIn(['c', 'd'], 1);
console.log(a.equals(b)); // true
console.log(Immutable.is(a, c)); // true
console.log(a === c); // false?
And for the final comparison I'd expect it to return true since I'm setting the path ['c', 'd'] to something else and then back to the original value, and with structural sharing I would expect that it results in c holding a reference to the original data structure?
Do I mis-understand how this works?

First, this one console.log(a.equals(b)); returns false actually:
Now for your question, as documented in Immutable.js here at "Return self on no-op optimization" sub-chapter:
When possible, Immutable.js avoids creating new objects for updates
where no change in value occurred, to allow for efficient reference
equality checking to quickly determine if no change occurred.
There's that example:
const { Map } = require('immutable');
const originalMap = Map({ a: 1, b: 2, c: 3 });
const updatedMap = originalMap.set('b', 2);
updatedMap === originalMap; // No-op .set() returned the original reference.
However updates which do result in a change will return a new
reference. Each of these operations occur independently, so two
similar updates will not return the same reference:
And that example:
const { Map } = require('immutable');
const originalMap = Map({ a: 1, b: 2, c: 3 });
const updatedMap = originalMap.set('b', 1000);
// New instance, leaving the original immutable.
updatedMap !== originalMap;
const anotherUpdatedMap = originalMap.set('b', 1000);
// Despite both the results of the same operation, each created a new reference.
anotherUpdatedMap !== updatedMap;
// However the two are value equal.
anotherUpdatedMap.equals(updatedMap);
Since you are changing the value, setIn returns a new reference. Therefore they are not equal by reference.
Hope I helped :)

Related

Google Place Autocomplete - geometry bounds properties

I cannot find this answer in the Google docs so I am posting my question here.
As per the Google Places Autocomplete response docs, bounds is one of the returned fields.
However, when console logging bounds in the browser, the object has strangely named keys such as Ba, Ab, Ra and so on..
Furthermore, I have noticed that these keys change overtime.
For example, the following code might fail within a few days. In the initial search for say, New York, bounds.Ab.g may contain a number value.
however, after a few days bounds.Ab.g might become bounds.Bb.g and the original value will be undefined.
import PlacesAutocomplete, { geocodeByAddress } from 'react-places-autocomplete'
const GooglePlacesSearchBar = () => {
const handleSelect = async userInput => {
const result = await geocodeByAddress(userInput)
const { place_id, geometry, formatted_address } = result[0]
const { bounds } = geometry
const swBounds = [bounds.Ab.g, bounds.Ra.g]
const neBounds = [bounds.Ab.h, bounds.Ra.h]
...
}
This is an example of the bounds object printed in console.
bounds: _.Wf
Bb: Vf
g: 49.19817700000001
h: 49.3172939
[[Prototype]]: Object
Ra: Qf
g: -123.22474
h: -123.023068
Could anyone point to a doc or explain what these keys stand for and why they keep changing?
thanks to #geocodezip for the answers in the comments section.
here is the solution
const result = await geocodeByAddress(userInput)
const { geometry } = result[0]
const { bounds } = geometry
const NELat = bounds.getNorthEast().lat()
const NELng = bounds.getNorthEast().lng()
const SWLat = bounds.getSouthWest().lat()
const SWLng = bounds.getSouthWest().lng()
Strange decision by Google indeed.

Google Slides: newly inserted table not found

I´m wondering what is going on. I have two functions which both are working good when called one after one:
function createTable() {
var slidesPage = SlidesApp.openById('1QWRV4eQzGNNBz4SkR3WPurTL3O60oGYxQpBu63KrUoI').getSlides()[0];
var table = slidesPage.insertTable(7, 4);
}
function changeColumnWidth() {
var slidesPage = SlidesApp.openById('1QWRV4eQzGNNBz4SkR3WPurTL3O60oGYxQpBu63KrUoI').getSlides()[0];
var tableId = slidesPage.getTables()[0].getObjectId();
var requests = [{
updateTableColumnProperties: {
objectId: tableId,
"columnIndices": [ 1, 3],
"tableColumnProperties": {
"columnWidth": {
"magnitude": 80,
"unit": "PT"
}
},
"fields": "columnWidth"
}
}];
var createSlideResponse = Slides.Presentations.batchUpdate({
requests: requests
}, '1QWRV4eQzGNNBz4SkR3WPurTL3O60oGYxQpBu63KrUoI');
}
But trying to combine these two functions like:
function combined() {
createTable();
changeColumnWidth();
}
I´m getting Error:
Invalid requests[0].updateTableColumnProperties: The object (SLIDES_API456304911_0) could not be found.
Wondering if the insertTable method is asynchronous and therefore the created table is not ready?
Thanks for any help.
How about this modification? Please think of this as one of several workarounds. In my workaround, I used saveAndClose() for your situation. Using this, I thought to separate the process of SlidesApp and Slides API.
Modification points :
Save and close the slide using saveAndClose() after the table was inserted.
Return an object ID of inserted table to use at changeColumnWidth().
At changeColumnWidth(), the table is modified by Slides API using the received object ID.
Modified script :
function combined() {
var tableId = createTable(); // Modified
changeColumnWidth(tableId); // Modified
}
function createTable() {
var slide = SlidesApp.openById('1QWRV4eQzGNNBz4SkR3WPurTL3O60oGYxQpBu63KrUoI'); // Modified
var slidesPage = slide.getSlides()[9]; // Modified
var table = slidesPage.insertTable(7, 4);
slide.saveAndClose(); // Added
return table.getObjectId();
}
function changeColumnWidth(tableId) { // Modified
// var slidesPage = SlidesApp.openById('1QWRV4eQzGNNBz4SkR3WPurTL3O60oGYxQpBu63KrUoI').getSlides()[0]; // This line is not used.
// var tableId = slidesPage.getTables()[0].getObjectId(); // This line is not used because slidesPage.getTables().length becomes 0.
var requests = [{
updateTableColumnProperties: {
objectId: tableId,
"columnIndices": [ 1, 3],
"tableColumnProperties": {
"columnWidth": {
"magnitude": 80,
"unit": "PT"
}
},
"fields": "columnWidth"
}
}];
var createSlideResponse = Slides.Presentations.batchUpdate({
requests: requests
}, '1QWRV4eQzGNNBz4SkR3WPurTL3O60oGYxQpBu63KrUoI');
}
Note :
For the slide which is saved and closed by saveAndClose(), when the slide is reopened, the inserted table cannot be retrieved. When the table is tried to be retrieved using getTables() again, the length becomes 0. But at Slides API, the object ID of table can be retrieved. So I thought that the issue might be able to be solved by returning the object ID of table after the table was inserted.
But I couldn't understand about the reason that the values retrieved by getTables() from the reopened Slide become "0" yet. I'm sorry.
Reference :
saveAndClose()
If this workaround was not what you want, I'm sorry.
To achieve your goal - create a table with a specified layout and specific column sizes in one function - you should use the Slides API for the entire task. The Slides API lets you both create and modify the same element in the same batch request, if you provided a unique object ID for it. Otherwise, you have to first create the element, then send the modification request using the objectId found in the response to the first request. This second approach is essentially the behavior you were experiencing when the function calls were done separately.
There are restrictions on user-supplied IDs, naturally:
objectId string: A user-supplied object ID.If you specify an ID, it must be unique among all pages and page elements in the presentation. The ID must start with an alphanumeric character or an underscore (matches regex [a-zA-Z0-9_] ); remaining characters may include those as well as a hyphen or colon (matches regex [a-zA-Z0-9_-:] ). The length of the ID must not be less than 5 or greater than 50.If you don't specify an ID, a unique one is generated.
Given that hyphens are allowed, we can use the Utilites.getUuid() method to help supply our own unique object IDs.
When mixing SlidesApp and Slides, it is very likely that internal Google optimizations (e.g. write-caching) change the operation order. By restricting to a single service for related task operations, we can ensure that the objects we need are available when needed.
This example uses two methods that make Request objects for batchUpdate and ultimately creates a presentation, adds a blank slide, adds a table and modifies it, and then creates another blank slide.
function makeCreateTableRequest_(slideId, rows, columns, shouldSupplyID) {
const tablerq = {
rows: rows,
columns: columns,
elementProperties: {
pageObjectId: slideId,
/** size: {
height: {...},
width: {...}
},
transform: { ... } */
}
};
// If asked to use a custom ID (e.g. also going to modify this table), use a unique one.
if (shouldSupplyID)
tablerq.objectId = ("table" + Utilities.getUuid()).slice(0, 50);
return {createTable: tablerq};
}
function makeModifyTableColumnPropsRequest_(tableId, newWidthDimension, indicesArray) {
const rq = {
objectId: tableId,
fields: "columnWidth" // There are no other fields for this request as of 2018-07
};
if (newWidthDimension && newWidthDimension.magnitude !== undefined && newWidthDimension.unit)
rq.tableColumnProperties = { columnWidth: newWidthDimension };
if (indicesArray && indicesArray.length)
rq.columnIndices = indicesArray;
return {updateTableColumnProperties: rq};
}
function createPresentation_() {
const newPres = { title: "API-created Presentation" };
// Presentations are huge... limit the metadata sent back to us.
const fields = "presentationId,pageSize,title"
+ ",slides(objectId,pageType,pageElements(objectId,size,title,description))"
+ ",masters(objectId,pageType,pageElements(objectId,size,title,description))"
+ ",layouts(objectId,pageType,pageElements(objectId,size,title,description))";
const createdMetadata = Slides.Presentations.create(newPres, {fields: fields});
console.log({message:"Created a Presentation", response: createdMetadata});
return createdMetadata;
}
function addSlide_(pId) {
const response = Slides.Presentations.batchUpdate({ requests: [{ createSlide: {} }] }, pId);
return response.replies[0].createSlide.objectId;
}
function foo() {
const pres = createPresentation_();
const newSlideId = addSlide_(pres.presentationId);
// Get requests to add and to modify tables.
const openingTableRq = makeCreateTableRequest_(pres.slides[0].objectId, 2, 4);
const newTableRq = makeCreateTableRequest_(newSlideId, 7, 4, true);
const changeWidthRq = makeModifyTableColumnPropsRequest_(newTableRq.createTable.objectId, {magnitude: 80, unit: "PT"}, [0]);
// Add and update the desired table, then create a new slide.
var response = Slides.Presentations.batchUpdate({
requests: [
openingTableRq, // will have reply
newTableRq, // will have reply
changeWidthRq, // no reply
{ createSlide: {} } // will have reply
]
}, pres.presentationId);
console.log({message: "Performed updates to the created presentation", response: response});
}

Functions being returned as keys

Newbie question here...I'm building a simple stack using a functional pattern and returning the push and pop functions as keys(I ofcourse don't want this)...I really am not sure why. The function is operational, just returning those two extra keys...
This is what the return looks like...
{ size: 2,
storage: { '1': 'test0', '2': 'test1' },
push: [Function], <== don't want
pop: [Function] } <== don't want
[Finished in 0.1s]
function Stack () {
var obj = {};
obj.size = 0;
obj.storage = {};
obj.push = function(data) {
var newSize = ++obj.size;
obj.storage[newSize] = data;
};
obj.pop = function() {
var newSize = obj.size;
var deletedData;
if (newSize) {
deletedData = obj.storage[newSize];
delete obj.storage[newSize];
obj.size--;
return deletedData;
}
};
return obj;
};
var stack = new Stack();
stack.push('test0')
stack.push('test1')
stack.push('test2')
stack.pop()
console.log(stack)
You say "obviously" you don't want the object to include keys for the functions, but I guess to me it's not so obvious... So what is it you do want to end up with? In JavaScript a function reference is just another piece of data, and a method is just a function reference stored as the value for some key on an object.
If you want the user to be able to say obj.push(...) then you do want a push key on obj (so that obj.push means something), and you want its value to be a function (so that the () operator can be applied to it).
Now I am curious because your output block says the keys are xPush and xPop but those aren't the values you show in the code block. Is that because of editing in the question? If not I don't see how that could be the way you've shown it.

Using Q to return secondary query in node with express and mysql

New to node, As I am cycling through a roster of students, I need to check and see if a teacher has requested them for tutoring.
I realized I can't just do this:
var checkRequest = function(id){
var value = '';
roster.query('SELECT * FROM teacher_request WHERE student_id ='+id, function(err, row){
value = row.length;
}
return value;
}
After a bit of digging around promises looked like a great solution, but if I simply return the deferred.promise from the checkRequest function, all I get is an object that says [deferred promise] which I can't access the actual data from. (Or have not figured out how yet)
If I follow along with their api and use .then (as illustrated in the getRow) function, I am back in the same problem I was in before.
function checkRequest(id) {
console.log(id);
var deferred = Q.defer();
connection.query('SELECT * FROM teacher_request WHERE student_id ='+id, function(err, row){
deferred.resolve(row.length);
});
return deferred.promise;
}
var getRow = function(id){
checkRequest(id).then(function(val) {
console.log(val); // works great
return val; //back to the same problem
});
}
The roster needs to be able to be pulled from an external API which is why I am not bundling the request check with the original roster query.
Thanks in advance
From the stuff you posted, I assume you have not really understood the concept of promises. They allow you to queue up callbacks, that get executed, when the asynchronous operation has finished (by succeeding or failing).
So instead of somehow getting the results back to your synchronous workflow, you should convert that workflow to work asynchronous as well. So a small example for your current problem:
// your students' ids in here
var studentsArray = [ 1, 2, 5, 6, 9 ];
for( var i=0; i<studentsArray.length; i++ ) {
checkRequest( i )
.then( function( data ){
console.log( data.student_id );
// any other code related to a specific student in here
});
}
or another option, if you need all students' data at the same time:
// your students' ids in here
var studentsArray = [ 1, 2, 5, 6, 9 ];
// collect all promises
var reqs = [];
for( var i=0; i<studentsArray.length; i++ ) {
reqs.push( checkRequest( i ) );
}
Q.all( reqs )
.then( function(){
// code in here
// use `arguments` to access data
});

Difference between json.js and json2.js

Can someone tell me what the difference is between the 2 JSON parsers?
https://github.com/douglascrockford/JSON-js/blob/master/json.js
https://github.com/douglascrockford/JSON-js/blob/master/json2.js
I have a JSON file from 2007-04-13 (It has methods such as parseJSON). I don't see these methods in any of the new versions.
From their code:
// Augment the basic prototypes if they have not already been augmented.
// These forms are obsolete. It is recommended that JSON.stringify and
// JSON.parse be used instead.
if (!Object.prototype.toJSONString) {
Object.prototype.toJSONString = function (filter) {
return JSON.stringify(this, filter);
};
Object.prototype.parseJSON = function (filter) {
return JSON.parse(this, filter);
};
}
I guess parseJSON is obsolete, therefore the new version (json2) doesn't even use it anymore. However if your code uses parseJSON a lot you could just add this piece of code somewhere to make it work again:
Object.prototype.parseJSON = function (filter) {
return JSON.parse(this, filter);
};
Quoting here:
"JSON2.js - Late last year Crockford quietly released a new version of his JSON API that replaced his existing API. The important difference was that it used a single base object."
I also noticed that json2 stringified arrays differently than json2007.
In json2007:
var array = [];
array[1] = "apple";
array[2] = "orange";
alert(array.toJSONString()); // Output: ["apple", "orange"].
In json2:
var array = [];
array[1] = "apple";
array[2] = "orange";
alert(JSON.stringify(array)); // Output: [null, "apple", "orange"].