Infinite functions calls like 'string'.replace().replace() - javascript

I'm not really sure how to explain so I will start with the output.
I need to return this:
{
replies:
[
{ type: 'text', content: 'one' }
{ type: 'text', content: 'two' }
{ type: 'text', content: 'three' }
],
conversation: {
memory
}
}
And I wanted to return that through in-line statement.
So I would like to call something like:
reply.addText('one').addText('two').addText('three').addConversation(memory)
Note that addText can be called infinite times while addConversation can be called only one time. Also conversation is optional, in that case, if conversation is absent the conversation object should not appear in the output.

To create a custom structured object use a constructor, say Reply.
To call instance methods on the return value of method calls, return the instance object from the method.
Choices to prevent multiple additions of conversation objects include throwing an error (as below) or perhaps logging a warning and simply not add additional objects after a first call to addConversation.
Write the code to implement the requirements.
For example using vanilla javascript:
function Reply() {
this.replies = [];
}
Reply.prototype.addText = function( content) {
this.replies.push( {type: "text", content: content});
return this;
}
Reply.prototype.addConversation = function( value) {
if( this.conversation) {
//throw new Error("Only one conversation allowed");
}
this.conversation = {conversation: value};
return this;
};
Reply.prototype.conversation = null;
// demo
var reply = new Reply();
reply.addText( "one").addText("two").addConversation("memory?");
console.log( JSON.stringify( reply, undefined," "));
(The console.log uses JSON stringify to avoid listing inherited methods)

A possible implementation is to create a builder as follows:
function create() {
const replies = []; // store all replies in this array
let conversation; // store the memory here
let hasAddConversationBeenCalled = false; // a state to check if addConversation was ever called
this.addText = function(content) {
// add a new reply to the array
replies.push({
type: 'text',
content
});
return this; // return the builder
};
this.addConversation = function(memory) {
if (!hasAddConversationBeenCalled) { // check if this was called before
// if not set the memory
conversation = {
memory
};
hasAddConversationBeenCalled = true; // set that the memory has been set
}
return this; // return the builder
}
this.build = function() {
const reply = {
replies
};
if (conversation) { // only if converstation was set
reply.conversation = conversation; // add it to the final reply object
}
return reply; // finally return the built respnse
}
return this; // return the new builder
}
You can then use it as follows:
const builder = create();
const reply = builder.addText('one').addText('two').addText('three').addConversation({}).build();
Here is a link to a codepen to play around with.

If you specifically want to add assemble this via multiple function calls, then the builder pattern is your best bet, as vader said in their comment.
However, if the goal is to simply create shorthand for concisely building these objects, it can be done using a function that takes the list of text as an array.
const buildObject = (textArray, memory) => {
return Object.assign(
{},
{
replies: textArray.map(x => {
return {
type: 'text',
value: x
}
})
},
memory ? {conversation: memory} : null
)
}
var memory = { };
//with memory
console.log(buildObject(['one', 'two', 'three'], memory ))
//without memory
console.log(buildObject(['one', 'two', 'three']));
Fiddle example: http://jsfiddle.net/ucxkd4g3/

Related

How to bind multiple properties in one calculate in GoJS?

I have a simple demo like the one below:
const calcProperty = (data) => {
//Some complex calculate
return {
text: '...',
stroke: "..."
}
}
const calcText = (data) => {
return calcProperty(data).text;
}
const calcStroke = (data) => {
return calcProperty(data).stroke;
}
$(go.TextBlock, {
text: 'refresh', //Default
stroke: 'red', // Default
},
new go.Binding("text", "", calcText),
new go.Binding("stroke", "", calcStroke),
),
As you can see, the complex calculations have to run twice.
Is that anyway I can do something like this, and it automatic to apply to properties?
new go.Binding("dynamic or something", "", (data) => {
//Some complex calculate
return {
text: '...',
stroke: "..."
}
}),
You could set the properties, once calculated, on the Node's data object, and also set a flag.
const calcProperty = (data) => {
if (data.calculated) return data; // already run
data.calculated = true;
//Some complex calculations that ALSO set:
data.text = "...";
data.stroke = "...";
return data;
}
Of course, you will need to handle cache invalidation if you go this route.
Note also that empty-string bindings re-run every time any of the data changes. If this is intended to be a one-time thing it shouldn't be a data binding at all, it should be computed before the node data is even added to the model, and the data bindings should be simple bindings instead.

Load data in the object?

I am not sure if i'm doing the right approach, I am doing like class style. Is there a way to load data in the object using loadProducts(data) so then I can call orderLines.getItemsType()
const orderProducts = {
loadProducts: function(data) {
//Load data into orderProducts object?
},
getItemsType: function(type) {
// return data
}
};
Usage:
const items = orderProducts.getItemsType(['abc', 'ddd']);
Note: It is for node.js, not for the browser.
First you want to save the products into a property. We will load the property with some dummy data.
We can then filter the data using filter and test if the item is in the products array like this:
const orderProducts = {
// The list of products
products: [],
// The products to load
loadProducts: function(...data) {
this.products.push(...data)
},
// Get items passed in
getItemsType: function(...type) {
return this.products.filter(p => type.includes(p))
}
}
orderProducts.loadProducts('abc', '123', '111', 'ddd')
const items = orderProducts.getItemsType('abc', 'ddd')
console.log(items)
I guess next approach can help you to make it class approach and solving your question:
class OrderProducts {
constructor(data) {
this.data = data;
this.getItemsType = this.getItemsType.bind(this);
}
getItemsType(type) {
// return the data filtering by type
return this.data;
}
}
// usage
const orderProduct = new OrderProduct(data);
const items = orderProduct.getItemsType(['abc', 'ddd']);

Javascript function to return Elasticsearch results

I'm trying to write a JavaScript function that returns results of an Elasticsearch v5 query. I can't figure out where and how to include 'return' in this code. With the following, segmentSearch(id) returns a Promise object,{_45: 0, _81: 0, _65: null, _54: null}.
_65 holds an array of the correct hits, but I can't figure out how to parse it. The console.log(hits) produces that same array, but how can I return it from the function?
var elasticsearch = require('elasticsearch');
var client = new elasticsearch.Client({
host: 'localhost:9200',
log: 'trace'
});
segmentSearch = function(id){
var searchParams = {
index: 'myIndex',
type: 'segment',
body: {
query: {
nested : {
path : "properties",
query : {
match : {"properties.source" : id }
},
inner_hits : {}
}
}
}
}
return client.search(searchParams).then(function (resp) {
var hits = resp.hits.hits;
console.log('hits: ',hits)
return hits;
}, function (err) {
console.trace(err.message);
});
}
I would instanitate a new array outside of your client.search function in global scope and array.push your 'hits' Then access your newly filled array.
let newArr = [];
client.search(searchParams).then(function (resp) {
for(let i = 0; i < resp.hits.hits.length; i++){
newArr.push(resp.hits.hits[i]);
}
console.log('hits: ',newArr)
return newArr;
}, function (err) {
console.trace(err.message);
});
First of all, elasticsearch js client is working with Promise ( I think using callback is also possible).
Using Promise is a good way to handle asynchronous computation.
In your question, you have already done something with a promise:
var search = function(id)
{
var searchParams = { /** What your search is **/}
return client.search(searchParams)
}
This call is returning a Promise.
If we consider handleResponse as the function in your then
var handleResponse = function(resp)
{
var hits = resp.hits.hits;
console.log('hits: ',hits)
return hits; //You could send back result here if using node
}
In your code, handleResponse is called once the promise is fullfield. And in that code you are processing data. Don't forget you are in asynchronious, you need to keep working with promise to handle hits.
By the way, in your question, what you have done by using "then" is chaining Promise. It is normal to have Promise.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then
Promise.prototype.then is returning a Promise.
var segmentSearch = function ( id )
{
return search
.then( handleResponse //here you got your hit )
.then( function ( hits )
{
console.log(hits) //and here you have done something with hits from search.
})
}
I neglected to post my fix, sorry about that. The sequence is to create searchParams, perform client.search(searchParams) which returns a Promise of hits, then process those hits:
segmentSearch = function(obj){
// retrieve all segments associated with a place,
// populate results <div>
let html = ''
var plKeys = Object.keys(obj)
var relevantProjects = []
for(let i = 0; i < plKeys.length; i++){
relevantProjects.push(obj[plKeys[i]][0])
var searchParams = {
index: 'myIndex',
type: 'segment',
body: {
query: {
nested : {
path : "properties",
query : {
match : {"properties.source" : id }
},
inner_hits : {}
}
}
}
}
client.search(searchParams).then(function (resp) {
return Promise.all(resp.hits.hits)
}).then(function(hitsArray){
...write html to a <div> using hits results
}
}

Call nested methods on object only if it exists using Ramda

I currently have a user object, which has a currentRoom() method on it, which might sometimes not exist or return null.
If the currentRoom() method returns something, I then need to call a messages() method on that. If nothing is returned from either, I’d like to return a default empty array [].
I’d like to tackle this functionally using Ramda, so it’s neat and reusable. Currently, my (non Ramda) code looks like this:
const user = Users.findOne({ username: params.id })
const room = (user.currentRoom && user.currentRoom() || {})
const messages = (room.messages && room.messages() || [])
Some kind of logic I was thinking was to pass in the list of required methods, along with the default result if nothing comes out of it.
/* getMessages(defaultIfNull, methodsArray, object) */
getMessages([], ['currentRoom', 'messages'], user)
Basically something a bit similar to pathOr, but for methods on objects.
I guess I'd use the List monad like an Option:
const getRoom = user => "currentRoom" in user ? [user.currentRoom()] : [];
const getMessages = room => "messages" in room ? room.messages() : [];
const allTogether = R.compose(R.chain(getMessage), R.chain(getRoom), R.of);
console.log(allTogether(Users.findOne({ username: params.id })));
You can use a point-free solution using just Ramda. First, we define a withDefaults function which will set up currentRoom and messages methods in objects which these methods are undefined.
var withDefaults = R.pipe(
// if don't have `currentRom` add a
// `currentRom` function that returns an empty object
R.unless(
R.hasIn('currentRoom'),
R.assoc('currentRoom',
R.always({}))),
// if don't have `messages` add a
// `messages` function that returns an empty array
R.unless(
R.hasIn('messages'),
R.assoc('messages',
R.always([]))))
This function filters the object setting up methods if needed. Usage example.
var user = withDefaults(getById(id))
Next, define a getter functions to get rooms and messages from objects. invoker is the central piece of this snipet, it returns a function that invokes a method. See http://ramdajs.com/docs/#invoker
var getCurrentRoom = R.invoker(0, 'currentRoom')
var getMessages = R.invoker(0, 'messages')
Above code can be used as following.
var userWithRoom = withDefaults({
currentRoom : function () {
return {
number : '123'
}
}
})
var userWithMessages = withDefaults({
messages : function () {
return [
'get lunch'
]
}
})
var userWithAll = withDefaults({
currentRoom : function () {
return {
number : '123'
}
},
messages : function () {
return [
'get lunch'
]
}
})
var userWithNone = withDefaults({})
All together
var withDefaults = R.pipe(
R.unless(
R.hasIn('currentRoom'),
R.assoc('currentRoom',
R.always({}))),
R.unless(
R.hasIn('messages'),
R.assoc('messages',
R.always([]))))
var getCurrentRoom = R.invoker(0, 'currentRoom')
var getMessages = R.invoker(0, 'messages')
// examples
var userWithRoom = withDefaults({
currentRoom : function () {
return {
number : '123'
}
}
})
var userWithMessages = withDefaults({
messages : function () {
return [
'get lunch'
]
}
})
var userWithAll = withDefaults({
currentRoom : function () {
return {
number : '123'
}
},
messages : function () {
return [
'get lunch'
]
}
})
Now lets test above code using console.log to see if our solution works as expected
console.log(getCurrentRoom(userWithRoom))
console.log(getCurrentRoom(userWithMessages))
console.log(getCurrentRoom(userWithAll))
console.log(getCurrentRoom(userWithNone))
console.log('---')
console.log(getMessages(userWithRoom))
console.log(getMessages(userWithMessages))
console.log(getMessages(userWithAll))
console.log(getMessages(userWithNone))
The output should be:
{ number: '123' }
{}
{ number: '123' }
{}
---
[]
[ 'get lunch' ]
[ 'get lunch' ]
[]

Is there a pattern for a lazy-loaded getter in a Collection-backed prototype (Building a reactive ORM relationship in JavaScript/Meteor)

I am trying to simulate a lazy-loaded array of items within a Collection-backed Meteor prototype, but with reactivity.
So, say I have a Collection of books w/ a Prototype:
Book = function(document) {
this._title = document.title;
this._author = document.author;
// ...
};
Books.prototype = {
get id() {
// Read-only
return this._id;
},
get title() {
return this._title;
},
set title(value) {
this._title = value;
},
// ...
};
Books = new Meteor.Collections('books', {
transform: function(doc) {
return new Book(doc);
}
});
And now I want to have a Shelves collection of Shelf, but I want to lazy-load the Books:
Shelf = function(document) {
this._location = document.location;
this._floor = document.floor;
// ...
this._book_ids = document.book_ids;
};
Shelf.prototype = {
get id() {
// Read-only
return this._id;
},
get location() {
return this._location;
},
set location(value) {
this._location = location;
},
// ...
get book_ids() {
// This returns an array of just the book's _ids
return this._book_ids;
},
set book_ids(values) {
this._book_ids = values;
// Set _books to "undefined" so the next call gets lazy-loaded
this._books = undefined;
},
get books() {
if(!this._books) {
// This is what "lazy-loads" the books
this._books = Books.find({_id: {$in: this._book_ids}}).fetch();
}
return this._books;
}
};
Shelves = new Meteor.Collections('shelves', {
transform: function(doc) {
return new Shelf(doc);
}
});
So, now I have a Self that I can now call Shelf.books on and get all of the Books, but they are not loaded until I call it. Furthermore, a call to set the book_ids will cause the data to be invalidated, so the next call to books yields the new set of Books associated with that Shelf.
Now how do I make this reactive such that updates to book_ids triggers a recall to finds the right Books, and doing so triggers anyone that has Shelf.books will now get triggered to refresh? Or, better yet, if a Book is updated then everything related to that Book (the Shelf.books and anyone who called it) gets reactively updated as well?

Categories

Resources