How do i get Delta to transform a "replace"-delta (eg., Delta.delete().insert()) instead of adding retain in between operations? - quill

Here is my case, i have a simple sentence: "I am good".
const initialSentence = new Delta().insert("I am good");
If two people at the same time do the following, A: enters a new word in the begging, B: change the first word in the sentence
const insertInitialWord = new Delta().insert("Therefore, ")
const changeToWe = new Delta().delete(4).insert("we are")
Now if i compose the two deltas:
const transformed = insertInitialWord.transform(changeToWe);
const result = initialSentence.compose(insertInitialWord).compose(transformed);
expect(toText(result)).toBe("Therefore, we are good");
I would expect the result to be "Therefore, we are good". But it is not. The result is "we areTherefore, good".
The reason for this is that a "retain"-operation is inserted between the insert and delete operation when transformed on the insertInitialWord delta.
Are there anyway i can Delta to transform the delete and insert statement in a bulk?

Related

Need to optimize the code for mapping codes to description

I have a Text field that has semicolon separated codes. These code has to be replaced with the description. I have separate map that have code and description. There is a trigger that replace the code with their description. the data will loaded using the dataloader in this field. I am afraid, it might not work for large amount of data since I had to use inner for loops. Is there any way I can achieve this without inner for loops?
public static void updateStatus(Map<Id,Account> oldMap,Map < Id, Account > newMap)
{
Map<String,String> DataMap = new Map<String,String>();
List<Data_Mapper__mdt> DataMapList = [select Salseforce_Value__c,External_Value__c from Data_Mapper__mdt where
active__c = true AND Field_API_Name__c= :CUSTOMFIELD_MASSTATUS AND
Object_API_Name__c= :OBJECT_ACCOUNT];
for(Data_Mapper__mdt dataMapRec: DataMapList){
DataMap.put(dataMapRec.External_Value__c,dataMapRec.Salseforce_Value__c);
}
for(Account objAcc : newMap.values())
{
if(objAcc.Status__c != ''){
String updatedDescription='';
List<String> delimitedList = objAcc.Status__c.split('; ');
for(String Code: delimitedList) {
updatedDescription = DataMap.get(Code);
}
objAcc.Status__c = updatedDescription;
}
It should be fine. You have a map-based access acting like a dictionary, you have a query outside of the loop. Write an unit test that populates close to 200 accounts (that's how the trigger will be called in every data loader iteration). There could be some concerns if you'd have thousands of values in that Status__c but there's not much that can be done to optimise it.
But I want to ask you 3 things.
The way you wrote it the updatedDescription will always contain the last decoded value. Are you sure you didn't want to write something like updatedDescription += DataMap.get(Code) + ';'; or maybe add them to a List<String> and then call String.join on it. It looks bit weird. If you truly want first or last element - I'd add break; or really just access the last element of the split (and then you're right, you're removing the inner loop). But written like that this looks... weird.
Have you thought about multiple runs. I mean if there's a workflow rule/flow/process builder - you might enter this code again. And because you're overwriting the field I think it'll completely screw you over.
Map<String, String> mapping = new Map<String, String>{
'one' => '1',
'two' => '2',
'three' => '3',
'2' => 'lol'
};
String text = 'one;two';
List<String> temp = new List<String>();
for(String key : text.split(';')){
temp.add(mapping.get(key));
}
text = String.join(temp, ';');
System.debug(text); // "1;2"
// Oh noo, a workflow caused my code to run again.
// Or user edited the account.
temp = new List<String>();
for(String key : text.split(';')){
temp.add(mapping.get(key));
}
text = String.join(temp, ';');
System.debug(text); // "lol", some data was lost
// And again
temp = new List<String>();
for(String key : text.split(';')){
temp.add(mapping.get(key));
}
text = String.join(temp, ';');
System.debug(text); // "", empty
Are you even sure you need this code. Salesforce is perfectly fine with having separate picklist labels (what's visible to the user) and api values (what's saved to database, referenced in Apex, validation rules...). Maybe you don't need this transformation at all. Maybe your company should look into Translation Workbench. Or even ditch this code completely and do some search-replace before invoking data loader, in some real ETL tool (or even MS Excel)

How to get an array of object from firestore in appscript

I have a set of documents in Firestore in this format. Questions array will 10 questions.
I want to get the data of questions field: one row for one question
I do I code in the appscript to perform this
This is my code so far (for one document only)
function test(){
const firestore = getFirestore();
var query = firestore.getDocument("QuestionCollection/test").fields;
var data = {};
data.subject = query.subject;
data.questions= query.questions;
const sheet = SpreadsheetApp.getActiveSheet();
for(i = 0 ; i < 10 (no. of question); i++){
const row = [data.questions[i].answer, data.questions[i].difficulty];
sheet.appendRow(row);
}
}
Error:
TypeError: Cannot read property 'answer' of undefined
Modification points:
When I saw your sample data in the image, it seems that the array length of questions is 2. But at the for loop, the end index of loop is 9. I think that by them, such error occurs.
When you want to put the value of "Serial", it is required to add the value for putting to Spreadsheet.
In your script, appendRow is used in a loop. In this case, the process cost becomes high.
When above points are reflected to your script, it becomes as follows.
Modified script:
Your for loop is modified as follows.
From:
for(i = 0 ; i < 10 (no. of question); i++){
const row = [data.questions[i].answer, data.questions[i].difficulty];
sheet.appendRow(row);
}
To:
var values = [];
for (var i = 0; i < data.questions.length; i++) {
const row = [i + 1, data.questions[i].answer, data.questions[i].difficulty];
values.push(row);
}
sheet.getRange(sheet.getLastRow() + 1, 1, values.length, values[0].length).setValues(values);
For the end index of loop, the array length is used.
Reference:
setValues(values)
You shouldn't query the .fields property directly (because your data isn't converted properly). Assuming you're using v28+ of the library, your code should look something like this:
function test(){
const firestore = getFirestore();
const query = firestore.getDocument("QuestionCollection/test").obj; // Don't use .fields here
const sheet = SpreadsheetApp.getActiveSheet();
const values = [["Serial", "Answer", "Difficulty"]]; // Init 2D array with Header row
// Loop through each question in the array and extract necessary values to construct Data rows
for (const question of query.questions){
values.push([query.questions.indexOf(question) + 1, question.answer, question.difficulty]);
}
// Replace 1, 1 below with coords of "Serial" header cell
const range = sheet.getRange(1, 1, values.length, values[0].length);
range.setValues(values);
// sheet.getRange(subjRow, subjCol).setValue(query.subject); // Add location for Subject data
}
I saw that you wanted "Serial" to represent "Question number", so I added that column to the header and data rows.
As Tanaike mentioned, there's a huge performance hit for writing to the spreadsheet in a loop, so it's better if you set up a 2D array of values to write all at once using range.setValues(array2D). Ideally, you'll want to minimize the calls to the Spreadsheet API.
Disclaimer: I'm an active contributor to the FirestoreGoogleAppsScript library.

Compare two big arrays value for value in Node.js

I have two arrays, one containing 200.000 product objects coming from a CSV file and one containing 200.000 product objects coming from a database.
Both arrays contains objects with the same fields, with one exception: the database objects have a unique ID as well.
I need to compare all 200.000 CSV objects with the 200.000 database objects. If the CSV object already exists in the database objects array I put it in an "update" array together with the ID from the match, and if it doesn't, then I put it in a "new" array.
When done, I update all the "update" objects in the database, and insert all the "new" ones. This goes fast (few seconds).
The compare step however takes hours. I need to compare three values: the channel (string), date (date) and time (string). If all three are the same, it's a match. If one of those isn't, then it's not a match.
This is the code I have:
const newProducts = [];
const updateProducts = [];
csvProducts.forEach((csvProduct) => {
// check if there is a match
const match = dbProducts.find((dbProduct) => {
return dbProduct.channel === csvProduct.channel && moment(dbProduct.date).isSame(moment(csvProduct.date), 'day') && dbProduct.start_time === csvProduct.start_time;
});
if (match) {
// we found a match, add it to updateProducts array
updateProducts.push({
id: match.id,
...csvProduct
});
// remove the match from the dbProducts array to speed things up
_.pull(dbProducts, match);
} else {
// no match, it's a new product
newProducts.push(csvProduct);
}
});
I am using lodash and moment.js libraries.
The bottleneck is in the check if there is a match, any ideas on how to speed this up?
This is a job for the Map collection class. Arrays are a hassle because they must be searched linearly. Maps (and Sets) can be searched fast. You want to do your matching in RAM rather than hitting your db for every single object in your incoming file.
So, first read every record in your database and construct a Map where the keys are objects like this {start_time, date, channel} and the values are id. (I put the time first because I guess it's the attribute with the most different values. It's an attempt to make lookup faster.)
Something like this pseudocode.
const productsInDb = new Map()
for (const entry in database) {
const key = { // make your keys EXACTLY the same when you load your Map ..
start_time: entry.start_time,
date: moment(entry.date),
entry.channel}
productsInDb.add(key, entry.id)
}
This will take a whole mess of RAM, but so what? It's what RAM is for.
Then do your matching more or less the way you did it in your example, but using your Map.
const newProducts = [];
const updateProducts = [];
csvProducts.forEach((csvProduct) => {
// check if there is a match
const key = { // ...and when you look up entries in the Map.
start_time: entry.start_time,
date: moment(entry.date),
entry.channel}
const id = productsInDb.get(key)
if (id) {
// we found a match, add it to updateProducts array
updateProducts.push({
id: match.id,
...csvProduct
});
// don't bother to update your Map here
// unless you need to do something about dups in your csv file
} else {
// no match, it's a new product
newProducts.push(csvProduct)
}
});

SWIFT: Copying objects from arrays then changing properties and adding them back to original array

I'm working on a SRS-based Flashcard program in swift. My problem (other than being new at programming, haha) is... once I alter the values of due date, score, etc; how do I add them back to the wordBank array keeping the new property values?
I started out creating a class for other flashcards to inherit properties like word, translation, sentence 1 & 2, audio URL, score (1-100 for scheduling purposes), etc; etc;
class Flashcard {
var id: Int = 0
var score: Int = 0
var word: String = ""
var sentence1: String = ""
// etc; etc;
From here, I created a new file and class called words, inheriting the Flashcard properties.
class Words: Flashcard {
// Example word 1
let you = Flashcard()
you.id = 0
you.score = 2
you.word = "khun"
you.sentence1 = "khun blah blah"
// Example word 2
let me = Flashcard()
you.id = 1
you.score = 1
you.word = "pohm"
you.translation = "Me or I"
// Create an array of all the words that will be in the program
var wordBank: [Flashcard] = [you, me, him, her, them, it] // etc;
Now, from here I can sort
// Sort array by lowest
wordBank.sort({ $1.score > $0.score })
// Create a new array for testing Session
var SessionArray: [Flashcard] = []
// Add the lowest scores to the SessionArray for testing
SessionArray += WordArray[0...6]
For the sake of remembering, My problem is once I alter the values of due date, score, etc; how do I add them back to the wordBank array keeping the new property values? I won't bother asking about how to persist this data right now, lol. I'm still reading about core data.
Thanks for any help you can give me!

Array Generating Images

I'm not overly sure if this is possible, as I am not a frequent programmer, but I have a question.
I've got an array that generates one random word in a text box and then a second array that generates another random word in a different text box. What I want is for when a certain word out of array number one is generated, a certain image appears with it. Here's the code:
var firstChoice:Array = ["Do this", "Do that", "Do something else"];
var secondOption:Array = ["while doing this", "while doing that", "while doing something else"];
generate_btn.addEventListener(MouseEvent.CLICK, getTask);
function getTask(event:MouseEvent):void {
var randomChoice:Number = Math.floor(Math.random() * firstChoice.length);
var randomOption:Number = Math.floor(Math.random() * secondOption.length);
Final_Choice.text = firstChoice[randomChoice];
Final_Option.text = secondOption[randomOption];
}
So for instance, when I click the button and the first array generates "Do this," I want a specific graphic to appear with it.
Hopefully this is possible :/ I'm stumped!
probably you need to use a HashMap, such as:
var map:Object = new Object();
map.first_choice = /*url of your image associated with this choice*/
map.second_choice = /*url of your image associated with this choice*/
//etc
and when a word is generated, you just compare the word with keys of the map, using a foreach, and get the url of your image

Resources