I have tried time and time again to utilize this same script & formatting to try to also get the "lost" checkbox to move to the "lost" tab (on the same script) and I've been at a loss. Any help on how to get two identical actions/scripts to work simultaneously? My current script allows the checkbox for "sold" to go in a "sold" tab, and i need the same for "lost" if that makes sense.
function onEdit(e) {
let sheet;
if (e.range.columnStart !== 9
|| e.range.rowStart === 2
|| !(sheet = e.range.getSheet()).getName().match(/^(Cynthia|Jenni|Kelsi|Monte)$/i)) {
return;
};
const targetSheet = SpreadsheetApp.getActive().getSheetByName('Sold');
const targetRange = targetSheet.getRange(targetSheet.getLastRow() + 1, 1);
sheet.getRange(e.range.rowStart, 1, 1, 8).moveTo(targetRange);
sheet.deleteRow(e.range.rowStart);
}
I think you should try changing
!(sheet = e.range.getSheet()).getName().match(/^(Cynthia|Jenni|Kelsi|Monte)$/i)
into
!(sheet === e.range.getSheet()).getName().match(/^(Cynthia|Jenni|Kelsi|Monte)$/i)
Related
I have 2 series (lines) displayed as shown in demo. I can have up to 30 series (lines) in one go.
What is implemented is, as soon as I move the mouse, it should update the current value (for all series (lines) - remember I can have up to 30 series in one go) in the grid shown right below stock chart. It is working as expected.
Pls find working demo : https://stackblitz.com/edit/react-ts-qcsum9?file=stockchart%2Fstockchart.js,constants%2Fzoom-level.js,stockchart%2FchartOptions.js
FYI: You can focus on only mouseOver function rather than looking in to all other files as demo has so many files.
Problem : Problem related to performance
As I told you I can have up to 30 series (lines) in one go. In given demo, there are two series(lines) and when you select 30 days, you have to move your mouse slowly in other to see updated or current hovered value in the gird. If you move your mouse speedily, it will not update the value immediately (I think that is obvious).
But the problem is on each mouse move, I'm looping through all available lines multiple times and perform some logic. I know it is a bit complex logic as shown in below code snippet. It runs every time when mouse is moved for each hovered point for all the lines. This is very bad way of updating the current value.
I know this is not the correct implementation but I really have NO IDEA how to optimize it. Any help would be greatly appreciated.
Code: Select all
point: {
events: {
mouseOver: (e) => {
const xAxisValue = e.target.x;
const chart = e.target.series.chart;
const series = chart.yAxis[0].series;
const totalGridSeries = [...stockGrid];
for (let i = 0; i < series.length; i++) {
const name = series[i].name || series[i].name;
const foundSeries = totalGridSeries.find(
(x) => x.name === name
);
if (foundSeries) {
if (xAxisValue) {
const foundPoint = series[i].points.find(
(t) => t.x === xAxisValue
);
if (foundPoint && foundPoint.y) {
foundSeries.currentValue = foundPoint.y;
} else {
foundSeries.currentValue = undefined;
}
} else {
foundSeries.currentValue = undefined;
}
}
}
setStockGrid(totalGridSeries);
},
},
}
I'm working on a sheet to build a list of products to import into Shopify.
For this, I have a pdf of some basic data (that is irrelevant here) out of which I build a string to crawl the product supplier's website and format the data in a way suitable for import in Shopify.
The products have a varying number of images (1 - 8), so I'm trying to build my script in a way that if a product has more than one image, I am trying to add additional rows under it and add every image past the first into a new row.
Here is my code:
function iterateThroughRows() {
// get spreadsheet
const sheet = SpreadsheetApp.getActive().getSheetByName("MySheet");
const data = sheet.getDataRange().getValues();
// Loop over rows
data.forEach( (row, rowIndex) => {
const imageSrcArray = [ /* list of image URLs, fetched from remote server */ ]
imageSrcArray.forEach( (img, arrayIndex) => {
if(arrayIndex == 0) { // for the first array item, just add it to the current row
const imageCell = sheet.getRange(rowIndex + 1, 24)
imageCell.setValue( imageSrcArray[arrayIndex] )
} else { // for each array item past the first one, add a new row and enter the value there
sheet.insertRows(rowIndex)
const imageCell = sheet.getRange(rowIndex + arrayIndex + 1, 24)
imageCell.setValue( imageSrcArray[arrayIndex] )
}
})
// adding some more values to other cells
});
}
As is this doesn't really work.
I worked on this all day yesterday and had a version using insertRowAfter() that did add additional rows, but added them all lumped together (i.e. there would be 15 rows after the first product, but none after any of the others). But since Google App Script doesn't have version control I lost that version.
I think the problem was that the forEach seems to move on to the newly created rows and keeps adding things from there rather than moving on to the initial next row.
So I'm more or less at the end of my wit with this. Any advise on how to properly do this would be highly appreciated.
I can understand your frustration, it is indeed because you care calculating the row based on the sheet in its version before you added new rows to it.
So my proposal would be to do this, as the currentRow allows you to track the current row you are working on. I also updated the insertRowAfter(), as I assume this is what you actually wanted to do.
let currentRow = 1;
data.forEach( (row, rowIndex) => {
const imageSrcArray = [ "img1URL", "img2URL"]
if( !imageSrcArray.length ) return
imageSrcArray.forEach( (img, arrayIndex) => {
if( arrayIndex == 0 ){
sheet.getRange(currentRow, 24).setValue( img )
} else {
sheet.insertRowAfter(currentRow)
sheet.getRange(currentRow+1, 24).setValue( img )
}
// New rows in between were created
currentRow++
})
});
Hi I have 2 spreadsheets, for example, one is called 'test' and the other 'test1'. I want when checkbox is checked for a row on spreadsheet 'test', the entire row moves to 'test1' spreadsheet. I used this script and it works perfectly if I want the row to move to another sheet in the 'test' spreadsheet.
function onEdit(e) {
const src = e.source.getActiveSheet();
const r = e.range;
if (src.getName() != "New" || r.columnStart != 6 || r.rowStart == 1) return;
const dest = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Complete");
src.getRange(r.rowStart,1,1,6).moveTo(dest.getRange(dest.getLastRow()+1,1,1,6));
src.deleteRow(r.rowStart);
}
But when I change it to this script so the row is moved from 'test' to 'test1' it does not work. I can't seem to figure out the solution. Can anyone help - thanks so much!
function onEdit(e) {
const src = e.source.getActiveSheet();
const r = e.range;
if (src.getName() != "New" || r.columnStart != 6 || r.rowStart == 1) return;
const dest = SpreadsheetApp.getActiveSpreadsheet("19D-PzL-
YGdRQbXdGeILMeUbHFJkxeMFan0di7eSqK6E").getSheetByName("Sheet1");
src.getRange(r.rowStart,1,1,6).moveTo(dest.getRange(dest.getLastRow()+1,1,1,6));
src.deleteRow(r.rowStart);
}
A piece of code I am working on consists of two functions - generateID and onEdit(e).
generateID - generates a number using pattern and it works fine.
onEdit(e) is the one I am having issue with.
In column A of a spreadsheet, a user selects a value from a dropdown (initially column A is empty, range is from row 2 to getLastRow(), row 1 for heading).
Once the value is selected, I need the result of the generateID to be inserted into the next column B of the same row.
If a value in coumn A is then changed (selected from dropdown), I need generateID to update its value.
Here is what I tried so far:
function onEdit(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var activeSheet = ss.getSheetByName("TEMP");
var range = activeSheet.getRange(2, 1, activeSheet.getLastRow(), 1).getValues();
for (var i =0; i < range.length; i ++) {
if (e.range [i][0] !== "") {
activeSheet.getRange(i + 2, 2, 1, 1).setValue(generateID());
};
};
};
onEdit seems to be the right choice to watch changes in the range. With (e) the code seems not to work at all. Trying it as onEdit() works but incorrectly.
Apprecaite any help on this.
Thank you.
e.range returns a range object and not a array1. Try this instead of your code:
e.value !=='' ? e.range.offset(0,1).setValue(generateID()) : null
I'm trying to process an array of JSON objects that have various common attributes, filtering each array entry on one or more of those attributes.
Normally, I'd just do it something like this:
let filteredResultsArray = originalArray.filter((obj) => {
return obj.attribute1 <= 3 && obj.attribute2 > 0 && obj.attribute3 === 10;
});
My problem is that the filter parameters (the part after "return" in the code above) are highly variable (and unpredictable) from run to run, so I can't hard-code them in the filter. I compute them on the fly and store the whole thing in a string in my code. For example, on one run it might be:
myAttributeString = "obj.attribute1 <= 3 && obj.attribute2 > 0 && obj.attribute3 === 10";
I've tried doing this:
let filteredResultsArray = originalArray.filter((obj) => {
return myAttributeString;
});
That's failing to filter anything. Apparently .filter() is not properly interpreting what I've stored in myAttributeString as filter criteria.
I have a sneaking suspicion that eval(myAttributeString) might be one way to pull this off, but unfortunately I'm working on a team where we've got tslint set to disallow the use of eval(), so that's not an option.
Anybody have an idea how I can get this to work?
When you "compute them on the fly", instead of creating a string, create a callback function that you can then pass to filter. For example, instead of
const myAttributeString = "obj.attribute1 <= 3 && obj.attribute2 > 0 && obj.attribute3 === 10";
do
const filterCallback = obj => obj.attribute1 <= 3 && obj.attribute2 > 0 && obj.attribute3 === 10
Then, later, when the appropriate time comes to .filter, simply pass that as the callback:
const filteredResultsArray = originalArray.filter(filterCallback);
If you can't pass functions around, another option would be to build an array of conditions, for example
[
{
prop: "attribute1",
constraint: "<=",
value: 3
},
{
prop: "attribute2",
constraint: ">",
value: 0
},
// ...
]
and then turn the object into the filter function needed.
****************************************UPDATE******************************
As I suspected, eval() did work, but since I can't use it in my delivered code, and thanks to CertainPerformance's suggestion (which put my thinking on the right track) as well as the Node.js documentation site (via a lucky Google search), I was able to find a workaround using the vm module:
import * as vm from "vm";
let filteredResultsArray = originalArray.filter(
vm.runInThisContext("(obj) => {
return " + myAttributeString + ";}"));
Case closed.