Looking to get info from an array inside an array, without having exact info basically.
local cfg_raids = {
[2] =
{
["10:17"] = {
raidName = "Rats - Thais",
Event_Type = "Raid Activated",
Storage = 1234,
alreadyExecuted = false
},
["10:20"] = {
raidName = "Testing this shit",
Event_Type = "Raid Activated",
Storage = 1235,
alreadyExecuted = false
},
},
[3] =
{
["12:00"] = {
raidName = "OrcsThais",
Event_Type = "Raid Activated",
Storage = 1236,
alreadyExecuted = false
},
},
Trying to Grab the time randomly without actually having the exact time stamp.
So like when the script activates the timestamp array ["10:17"] it grabs all the next arrays info ["10:20"] without actually knowing the ["10:20"]
OPEN TIBIA INFORMATION: http://otland.net/threads/looking-for-some-assistance-on-a-script.216303/
With non-integer keys like that you can't really do it. You can try using the next function to get the next key from your current key but you have no guarantees which next key you will get if there are more than two keys in the table (you can not even guarantee that it will be consistently the same next key).
You could use integer indices in that table and make time a field of the table and then simply use the next integer as your next key if that works however.
You could also store the times used as keys, in whatever order you want, in the integer indices in the table (or some other table) and use that without needing to redo the table itself (e.g. cfg_raids = { [2] = { "10:17", "10:20", ["10:17"] = {...}, ["10:20"] = {...} } }).
Related
So my trouble is that I have a collection of keys of certain records that I keep in my IndexedDb in the Chrome browser, the size of the store (aka table) is near 200 000, the set of keys is about 5 000.
There is no way to use indices, because the keys are very random.
What would be the fastest way to pull these 5000 records corresponding to the keys I have?
My current solution is to cursor through all records in the DB and check if each key is in the set. It's starting to be noticeably slow.
My current solution is to cursor through all records in the DB and check if each key is in the set. It's starting to be noticeably slow.
Rather than reading every record, read only the ones with matching keys (assuming by "key" you mean the key of the object store):
const sortedKeys = [1, 2, 3];
const output = [];
const range = IDBKeyRange.bound(sortedKeys[0], sortedKeys[sortedKeys.length - 1]);
let i = 0;
const request = objectStore.openCursor(range);
request.onsuccess = (event) => {
const cursor = event.target.result;
if (!cursor) {
console.log(output);
return;
}
output.push(cursor.value);
i += 1;
cursor.continue(sortedKeys[i]);
};
I have a set of data in a Google spreadsheet in two columns. One column is a list of article titles and the other is the ID of a hotel that is in that article. Call it list1.
Example data
I would like returned a new list with article titles in one column, and an array of the hotel IDs in that article in the other column. Call it list2.
Example data
There are thousands of lines that this needs to be done for, and so my hope was to use Google Apps Script to help perform this task. My original thinking was to
Create column 1 of list2 which has the unique article titles (no script here, just the G-sheets =unique() formula.
Iterate through the titles in list2, looking for a match in first column of the list1
If there is a match:
retrieve its corresponding value in column 2
push it to an empty array in column two of list2
move onto next row in list1
if no longer a match, loop back to step 2.
I've written the following code. I am currently getting a type error (TypeError: Cannot read property '0' of undefined (line 13, file "Code")), however, I wanted to ask whether this is even a valid approach to the problem?
function getHotelIds() {
var outputSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('list2');
var lastRow = outputSheet.getLastRow();
var data = outputSheet.getRange(2,1,lastRow,2).getValues();
var workingSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('list1');
var lastActiveRow = workingSheet.getLastRow();
var itemIDS = [];
for (var i=1; i<=data.length; i++) {
var currentArticle = data[i][0];
var lookupArticle = workingSheet[i][0];
if (currentArticle === lookupArticle) {
var tempValue = [workingSheet[i][1]];
itemIDS.push(tempValue);
}
}
}
Use a simple google sheets formula:
You can use a very simple formula to achieve your goal instead of using long and complicated scripts.
Use =unique(list1!A2:A) in cell A2 of list2 sheet to get the unique hotels.
and then use this formula to all the unique hotels by dragging it down in column B.
=JOIN(",",filter(list1!B:B,list1!A:A=A2))
You got the idea right, but the logic needed some tweaking. The "undefined" error is caused by the workingSheet[i][0]. WorkingSheet is a Sheet object, not an array of data. Also, is not necessary to get the data from list2 (output), it is rather the opposite. You have to get the data from the list1 (source) sheet instead, and iterate over it.
I added a new variable, oldHotel, which will be used to compare each line with the current hotel. If it's different, it means we have reached a different Hotel and the data should be written in list2.
function getHotelIds() {
var outputSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('list2');
var outLastRow = outputSheet.getLastRow();
var workingSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('list1');
var lastActiveRow = workingSheet.getLastRow();
var sourceValues = workingSheet.getRange("A2:B" + lastActiveRow).getValues();
var itemIDS = [];
var oldHotel = sourceValues[0][0]; //first hotel of the list
for (var i = 0; i < sourceValues.length; i++) {
if (sourceValues[i][0] == oldHotel) {
itemIDS.push(sourceValues[i][1]);
/*When we reach the end of the list, the oldHotel variable will never be different. So the next if condition is needed. Otherwise it wouldn't write down the last Hotel.
*/
if (i == sourceValues.length - 1) {
outputSheet.getRange(outLastRow + 1, 1, 1, 2).setValues([
[sourceValues[i][0], itemIDS.toString()]
]);
}
} else {
outputSheet.getRange(outLastRow + 1, 1, 1, 2).setValues([
[sourceValues[i - 1][0], itemIDS.toString()]
]);
oldHotel = sourceValues[i][0]; //new Hotel will be compared
outLastRow = outputSheet.getLastRow(); //lastrow has updated
itemIDS = []; //clears the array to include the next codes
}
}
}
I also converted the itemIDS array to a String each time, so it's written down in a single cell without issues.
Make sure each column of the Sheet is set to "Plain text" from Format > Number > Plain Text
References
getRange
setValues
toString()
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.
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)
}
});
players={
{uuid = "abc",name = "TheEisbaer"},
{uuid= "def",name ="Eisbaer68"},
{uuid= "ghj",name = "kevin"}
}
list={name = "TheEisbaer", name = "Eisbaer68"}
With print(list.name) it prints "Eisbaer68".
How do I get it to print "TheEisbaer"?
-
Can you do something like print(players[3].uuid) or how would one do that?
If I understand (and believe me, it's not easy) what you're trying to do:
First of all:
list={name = "TheEisbaer", name = "Eisbaer68"}
is equivalent to
list={name = "Eisbaer68"}
or, more clearly, perhaps:
list={}
list.name = "TheEisbaer"
list.name = "Eisbaer68"
As you use the same key twice in the same table, the most recent key value overwrites the previous one.
It seems you want to achieve the equivalent of a 'join' in databases. If you want to display (or, in general, filter) those 'records' from players that have their 'name' appear also in list, maybe something like this:
players =
{
{uuid= 'abc', name = 'TheEisbaer' },
{uuid= 'def', name = 'Eisbaer68' },
{uuid= 'ghj', name = 'kevin' },
}
list = { TheEisbaer=true, Eisbaer68=true }
for _,t in ipairs(players) do
if list[t.name] then --if key exists in list table ...
print(t.name .. ' => ' .. t.uuid)
end
end
Here the list has keys to make it simpler to lookup. You could also do it differently but it would probably require a nested FOR loop to go through all possible combinations between the two tables.