skip to Main Content

I want to update my question so as to better clarify my goal. Cooper’s
script works but doesn’t do exactly what I need. In column B I stored
data row by row so I could quickly check and compare the data with the
API. For this reason the comparison I would like to be made between
column B (json output) and the API, if possible. I also need to put
values in empty cells, I don’t need to replace existing values. As I
say later, I have many columns and I can not recreate the entire
array, also because the values could change in the API and be
different from the values I stored

Working Cooper’s answer

I have a datasheet that contains the ordered data. The sheet consists of thousands of rows identified by the order id.
In the second column I have all the data grouped as json string. The new values are added starting from the last row.

This operation works.

My problem starts when I have to update the changes of some values of ids.

The source where I parse data, both for append and update is a json api.

This is my simulation dataSheet:

Start dataSheet

enter image description here

A B C D E F G
1 string shipped timestamp id countitems destination delivery
2 {"id":"x11","shipped":null,"countItems":"21","destination":"Paris","delivery":null} x11 21 Paris
3 {"id":"x167","shipped":null,"countItems":"3","destination":"Lyon","delivery":null} x167 3 Lyon
4 {"id":"x89","shipped":null,"countItems":"8","destination":"Berlin","delivery":null} x89 8 Berlin
5 {"id":"x9811","shipped":null,"countItems":"2","destination":"Madrid","delivery":null} x9811 2 Madrid
6 {"id":"x73","shipped":null,"countItems":"9","destination":"Liverpool","delivery":null} x73 9 Liverpool

This is my current procedure:

1) Apply a filter to the json source, selecting the elements where "shipped" is yes.

// My API is for business use, so I can't share URL and token.
// Request and download work perfectly

const loadShipped = () => {return jsonApi().filter(k=>k.shipped == "yes")}

I get this output

var shipped = [
 {"id":"x167","shipped":"yes","countItems":"3","destination":"Lyon","delivery":"22/09/2023"}
,{"id":"x9811","shipped":"yes","countItems":"2","destination":"Madrid","delivery":"27/09/2023"}
]

2) Create a json output of column B and apply a filter selecting the elements where "shipped" is null.

const jsDataSheet = () => {
  const colJS = SpreadsheetApp.getActiveSpreadsheet().getRange("B5:B")
                              .getDisplayValues().filter(col=>col[0]);
  const data = `[${colJS.filter(o=>o!='').join(',')}]`;
  const dataSheetJson = [...new Set(JSON.parse(data))];
  return dataSheetJson.filter(k=>!k.shipped)
}

I get this output

var toUpdate = [
 {"id":"x167","shipped":null,"countItems":"3","destination":"Lyon","delivery":null}
,{"id":"x9811","shipped":null,"countItems":"2","destination":"Madrid","delivery":null}
]

cons: The columns are not adjacent and usually the two json have different length and sort.

This is the script I’m using, is very slow, this is the problem for me, I have too much data to update and many cells to fill. For this situation I have reduced the number of elements.

const sheet = SpreadsheetApp.getActiveSpreadsheet();

    for(var i=0;i<toUpdate.length;i++){
      for(var j=0;j<shipped.length;j++){
        if(toUpdate[i].id == shipped[j].id){

          const time = Date.parse(new Date()) / 1000;
          const ship = shipped[j].shipped;
          const dDel = shipped[j].delivery;

          const newJS = 
          JSON.stringify({...toUpdate[i]
          ,timestamp:time
          ,shipped:ship
          ,delivery:dDel
          });

          sheet.getRange(i + 5,2).setValue(newJS)
          sheet.getRange(i + 5,3).setValue(ship)
          sheet.getRange(i + 5,4).setValue(time)          
          sheet.getRange(i + 5,8).setValue(dDel)
        }
      }
    }

This is final result

Final result

string shipped timestamp id countItems destination delivery
{"id":"x11","shipped":null,"countItems":"21","destination":"Paris","delivery":null} x11 21 Paris
{"id":"x167","shipped":null,"countItems":"3","destination":"Lyon","delivery":null} yes x167 3 Lyon 22/09/2023
{"id":"x89","shipped":null,"countItems":"8","destination":"Berlin","delivery":null} x89 8 Berlin
{"id":"x9811","shipped":null,"countItems":"2","destination":"Madrid","delivery":null} yes x9811 2 Madrid 27/09/2023
{"id":"x73","shipped":null,"countItems":"9","destination":"Liverpool","delivery":null} x73 9 Liverpool

The target is to update data once, update string rows and put the values into empty cells, without changing values already added.

shared google link

2

Answers


  1. If you isolate the column with the id into a single flat array from your starting sheet and keeping all of the data in the array. Then that Array.indexOf(id from api) will give you the index into the array that you should be updating. Update all of those rows and use setValues again to reload the spreadsheet.

    function myfunk() {
      const shipped = [{ "id": "x167", "shipped": "yes", "countItems": "3", "destination": "Lyon", "delivery": "22/09/2023" }, { "id": "x9811", "shipped": "yes", "countItems": "2", "destination": "Madrid", "delivery": "27/09/2023" }];
      const sr = 2;
      const ss = SpreadsheetApp.getActive();
      const sh = ss.getSheetByName("Sheet0");//change sheet name and data start row
      let vs = sh.getRange(sr, 1, sh.getLastRow() - sr + 1, sh.getLastColumn()).getValues();
      const idA = vs.map((r, i) => r[3]);
      shipped.forEach((r, i) => {
        vs[idA.indexOf(shipped[i].id)] = [JSON.stringify(shipped[i]), "yes", new Date().valueOf(), shipped[i].id, shipped[i].countItems, shipped[i].destination, shipped[i].delivery];
      });
      sh.getRange(sr, 1, vs.length, vs[0].length).setValues(vs);
    }
    

    My output:

    RowCol A B C D E F G
    1 string shipped timestamp id countitems destination delivery
    2 {"id":"x11","shipped":null,"countItems":"21","destination":"Paris","delivery":null} x11 21 Paris
    3 {"id":"x167","shipped":"yes","countItems":"3","destination":"Lyon","delivery":"22/09/2023"} yes 1694983369773 x167 3 Lyon 22/09/2023
    4 {"id":"x89","shipped":null,"countItems":"8","destination":"Berlin","delivery":null} x89 8 Berlin
    5 {"id":"x9811","shipped":"yes","countItems":"2","destination":"Madrid","delivery":"27/09/2023"} yes 1694983369773 x9811 2 Madrid 27/09/2023
    6 {"id":"x73","shipped":null,"countItems":"9","destination":"Liverpool","delivery":null} x73 9 Liverpool
    Login or Signup to reply.
  2. Without much modifications to your script, this is how modify it to use arrays in place:

    const sheet =
      SpreadsheetApp.getActiveSpreadsheet().getSheetByName('[DATA SHEET]');
    const dataRange = sheet.getDataRange(),
      data = dataRange.getDisplayValues();
    for (let i = 0; i < toUpdate.length; i++) {
      for (let j = 0; j < shipped.length; j++) {
        if (toUpdate[i].id == shipped[j].id) {
          const time = Date.parse(new Date()) / 1000;
          const ship = shipped[j].shipped;
          const dDel = shipped[j].delivery;
    
          const newJS = JSON.stringify({
            ...toUpdate[i],
            timestamp: time,
            shipped: ship,
            delivery: dDel,
          });
    
          //Modify data in place in Array
          const iArray = 6;
          const updates = [newJS, ship, time, dDel];
          [2, 3, 4, 8]
            .map((c) => c + 1)
            .forEach((j, k) => (data[iArray][j] = updates[k]));
        }
      }
    }
    //set modified data
    dataRange.setValues(data);
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search