Working with Google Sheets

Hello everyone,

I have a Google Sheets spreadsheet that contains article topics, which are to be automatically created one by one.

The table includes the following columns:

  • “created” (contains “ok” if the article has been created, and is empty if it hasn’t been created yet)
  • “title”: contains the article’s headline
  • “category”: contains the category in which the article will be published
  • “info”: additional personal information, currently not in use

Now, a workflow should read the categories and randomly select one (this works). Then it should pick the first title that does not have an “ok” (i.e., an article that hasn’t been written yet in that category). Finally, after the article is created, it should automatically place an “ok” in the corresponding cell in the “created” column.

Unfortunately, the workflow doesn’t always find the first article without “ok” in the specific category. Afterward, I let it search for and count the position of the article in the JSON, but that doesn’t work reliably either (and also takes quite a long time). Ultimately, the goal is to use the position of the found row to generate the correct cell (e.g., A15) so that it can be updated with an “ok”.

My workflow is partly in German, partly in English—sorry about that:

https://app.mindstudio.ai/share/8BkRJktLq5D8

Are there easier ways to work with Google Sheets and update specific cells?

It seems that, with JSON for example, there’s no way to extract the position of an entry…

I’d appreciate any tips.

Thanks a lot!

You are correct that even the top models still struggle with things like counting the position of an item in a list (this falls in the category of other math-based struggles they have). I took a look at your agent and the approach generally seems sound, so unfortunately it might just be the case that the models aren’t very good at this type of problem right now, especially when the data/lists grow beyond a few items.

You can try using a model like Claude 4 with Reasoning enabled to see if that is able to do it, but your best bet would be to use some simple custom functions to do the filtering and searching. This will be dramatically faster, more reliable, and less expensive (effectively free vs. whatever the cost of running a large model with reasoning would be). This requires a bit of coding, but is otherwise super straightforward.

You would replace the Thema finden and subsequent steps with a custom function with the code:

const matchingRowIndex = ai.vars.content_websites.findIndex((row) => row[0] !== 'ok' && row[2] === ai.vars.choosenkat);
if (matchingRowIndex !== -1) {
  ai.vars.request = ai.vars.content_websites[matchingRowIndex][1]; // set the title
  ai.vars.tabzeile = matchingRowIndex + 1; // since javascript arrays start at 0 but spreadsheets start at 1, we need to add one here to get the correct sheet row
  ai.vars.zelle = `A${matchingRowIndex + 1}`;
}

Let me know if this helps. Sometimes code is a lot easier when it comes to things like this, even if it might seem more inaccessible at first!

Thank you very much for your help.
I honestly didn’t expect that this would be a problem for LLMs — to calculate things that (from my perspective) seem so simple.

And a thousand thanks for the code. It’s a bit overwhelming for me to understand at first, but thank you so much for writing it just for me — truly amazing support!!

I will integrate it.
Thanks
Frank

If your data is well structured and consistent, you could use custom functions as Sean mentioned. MindStudio supports both JS and Python. Alternatively, you could try the Ask Your Data block to return the first row without an “ok” in the column. Give this a try - it should work!

Thank you very much for the great help.

I’ve added the function, but unfortunately I’m getting an error message:

“ai.vars.content_websites.findIndex is not a function”

I did try to research the possible cause myself, but I don’t have enough experience with this to really figure it out on my own.

Regarding Jerry’s second idea of using the “Ask Your Data” block:

I’m not entirely sure what was meant here. Possibly the “Query Data Sauce” block?

But as far as I understand, I can’t query a variable with JSON content there. I would need a proper data source uploaded first, right?

Thanks again!

I just wanted to let you know that I was able to resolve the issue. With the help of Qwen.ai, I managed to optimize the code so that it’s now working.
Thanks again for your support!

Wonderful! Would you mind sharing a bit about your solution here for anyone else that might have a similar issue and discover this post?

Sure, with pleasure.

Here is the updated code.

First of all, the “FindIndex” error has been resolved.
(I can’t say exactly what the issue was, though — Qwen identified and fixed it.)

There are now some additional debug functions included.

Most importantly, the script now also checks whether an entire category has already been fully written. If so, it will be skipped.

Furthermore, the cell reference in which the “ok” needs to be placed (once the article has been written) is now being generated — including the full range string that is required as a variable in the “Update Google Sheets” node to dynamically update the cell (“sheetname!cell”).

The variable “Google Update Value”, which is also defined in this code, is not being used. I have handled this manually for now.

There is currently another issue: the Google Update node always deletes the content of the cell to the right of the cell that is supposed to be updated.
Because the node requires content under “spreadsheet content”, which assumes a comma-separated value (in my case “ok,”), a second (empty) content item is interpreted automatically — and this overwrites the second cell.

So it seems that it is not possible to update just a single cell.

Qwen suggested a few possible solutions, but none of them worked. That’s why there’s a value included in the code that was intended to be used as a variable.

However, I have now resolved this manually.
I’ve set the content to “ok,{{request}}”. This enters “ok” into the first cell, and the second cell — which actually should remain untouched — is now overwritten with the same value held in the variable.

So it works now, but it’s just a workaround.

If there is any info on how to properly update just a single cell, I’d be very happy to hear it. Maybe it’s a bug, or maybe it’s simply not possible in the node at the moment.

Here is the code:

// --- Dynamic selection of the first unmarked title ---
const sheetData = ai.vars.content_websites;
const sheetName = "test"; // ⚠️ Replace with your sheet name!

if (!sheetData || !sheetData.values || !Array.isArray(sheetData.values)) {
  console.error("Error: Invalid sheet data", sheetData);
  ai.vars.request = null;
  ai.vars.choosenkat = null;
  ai.vars.tabzeile = null;
  ai.vars.zelle = null;
  ai.vars.google_update_range = null;
  return;
}

const rows = sheetData.values;

// 1. Find all categories that have at least one unmarked title (A ≠ "ok")
const categoriesWithPending = [...new Set(
  rows
    .slice(1)
    .filter(row => {
      if (!Array.isArray(row) || row.length < 3) return false;
      const status = (row[0] || "").toString().trim();
      const category = (row[2] || "").toString().trim();
      return category && status !== "ok";
    })
    .map(row => row[2].toString().trim())
)];

// 2. No pending titles?
if (categoriesWithPending.length === 0) {
  console.warn("All titles have already been processed.");
  ai.vars.choosenkat = null;
  ai.vars.request = null;
  ai.vars.tabzeile = null;
  ai.vars.zelle = null;
  ai.vars.google_update_range = null;
  return;
}

// 3. Select a random category
const chosenCategory = categoriesWithPending[Math.floor(Math.random() * categoriesWithPending.length)];
ai.vars.choosenkat = chosenCategory;

// 4. Find the first unmarked title in this category
const matchingRowIndex = rows.findIndex((row, index) => {
  if (index === 0) return false; // Skip header
  if (!Array.isArray(row) || row.length < 3) return false;
  const status = (row[0] || "").toString().trim();
  const category = (row[2] || "").toString().trim();
  return category === chosenCategory && status !== "ok";
});

// 5. Save result
if (matchingRowIndex !== -1) {
  ai.vars.request = rows[matchingRowIndex][1];           // Title
  ai.vars.tabzeile = matchingRowIndex + 1;               // Row number (1-based)
  ai.vars.zelle = `A${matchingRowIndex + 1}`;            // Cell A + row
  ai.vars.google_update_range = `${sheetName}!${ai.vars.zelle}`; // e.g. "test!A24"

  console.log("✅ Title:", ai.vars.request);
  console.log("📝 Cell to be marked:", ai.vars.google_update_range);
} else {
  console.error("❌ No title found – logic error.");
  ai.vars.request = null;
  ai.vars.tabzeile = null;
  ai.vars.zelle = null;
  ai.vars.google_update_range = null;
}

// Debug output
console.log("Cell:", ai.vars.zelle);
console.log("Range:", ai.vars.google_update_range);
console.log("Category:", ai.vars.choosenkat);
console.log("Title:", ai.vars.request);

// Set values for Google Sheets update
ai.vars.google_update_values = [["ok"]];
ai.vars.google_update_range = `${sheetName}!${ai.vars.zelle}`; // e.g. test!A9

// Debug output
console.log("📤 google_update_values (Type):", typeof ai.vars.google_update_values);
console.log("📋 Content:", ai.vars.google_update_values);