Google Sheets API v4 - How to get the last row with value? - android

How to get the last row with value in the new Google Sheets API v4 ?
i use this to get a range from a sheet:
mService.spreadsheets().values().get("ID_SHEET", "Sheet1!A2:B50").execute();
how to detect the last row with value in the sheet ?

You can set the range to "A2:D" and this would fetch as far as the last data row in your sheet.

I managed to get it by counting the total rows from current Sheets.
Then append the data to the next row.
rowcount = this.mService.spreadsheets().values().get(spreadsheetId, range).execute().getValues().size()

Rather than retrieving all the rows into memory just to get the values in the last row, you can use the append API to append an empty table to the end of the sheet, and then parse the range that comes back in the response. You can then use the index of the last row to request just the data you want.
This example is in Python:
#empty table
table = {
'majorDimension': 'ROWS',
'values': []
}
# append the empty table
request = service.spreadsheets().values().append(
spreadsheetId=SPREADSHEET_ID,
range=RANGE_NAME,
valueInputOption='USER_ENTERED',
insertDataOption='INSERT_ROWS',
body=table)
result = request.execute()
# get last row index
p = re.compile('^.*![A-Z]+\d+:[A-Z]+(\d+)$')
match = p.match(result['tableRange'])
lastrow = match.group(1)
# lookup the data on the last row
result = service.spreadsheets().values().get(
spreadsheetId=SPREADSHEET_ID,
range=f'Sheetname!A{lastrow}:ZZ{lastrow}'
).execute()
print(result)

😢 Google Sheets API v4 does not have a response that help you to get the index of the last written row in a sheet (row that all cells below it are empty). Sadly, you'll have to workaround and fetch all sheet rows' into memory (I urge you to comment if I'm mistaken)
Example:
spreadsheet_id = '1TfWKWaWypbq7wc4gbe2eavRBjzuOcpAD028CH4esgKw'
range = 'Sheet1!A:Z'
rows = service.spreadsheets().values().get(spreadsheetId=spreadsheet_id, range=range).execute().get('values', [])
last_row = rows[-1] if rows else None
last_row_id = len(rows)
print(last_row_id, last_row)
Output:
13 ['this', 'is ', 'my', 'last', 'row']
💡 If you wish to append more rows to the last row, see this

You don't need to. Set a huge range (for example A2:D5000) to guarantee that all your rows will be located in it. I don't know if it has some further impact, may be increased memory consumption or something, but for now it's OK.
private List<String> getDataFromApi() throws IOException {
String spreadsheetId = "1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms";
String range = "A2:D5000";
List<String> results = new ArrayList<String>();
ValueRange response = this.mService.spreadsheets().values()
.get(spreadsheetId, range)
.execute();
List<List<Object>> values = response.getValues();
if (values != null) {
results.add("Name, Major");
for (List row : values) {
results.add(row.get(0) + ", " + row.get(3));
}
}
return results;
}
Look at the loop for (List row : values). If you have two rows in your table you will get two elements in values list.

Have a cell somewhere that doesn't interfere with your datarange with =COUNTA(A:A) formula and get that value.
In your case
=MAX(COUNTA(A:A50),COUNTA(B:B50))
?
If there could be empty cells inbetween the formula would be a little more tricky but I believe it saves you some memories.

2022 Update
I I don’t know if this will be relevant for someone in 2022, but now you can do it differently.
You can just set next value as range :
const column = "A"
const startIndex = 2
const range = column + startIndex + ":" + column
In resolve you get all data in column and range with last index.
I tested it on js and php

Following Mark B's answer, I created a function that performs a dummy append and then extracts the last row info from the dummy append's response.
def get_last_row_with_data(service, value_input_option="USER_ENTERED"):
last_row_with_data = '1'
try:
dummy_request_append = service.spreadsheets().values().append(
spreadsheetId='<spreadsheet id>',
range="{0}!A:{1}".format('Tab Name', 'ZZZ'),
valueInputOption='USER_ENTERED',
includeValuesInResponse=True,
responseValueRenderOption='UNFORMATTED_VALUE',
body={
"values": [['']]
}
).execute()
a1_range = dummy_request_append.get('updates', {}).get('updatedRange', 'dummy_tab!a1')
bottom_right_range = a1_range.split('!')[1]
number_chars = [i for i in list(bottom_right_range) if i.isdigit()]
last_row_with_data = ''.join(number_chars)
except Exception as e:
last_row_with_data = '1'
return last_row_with_data

Related

Given an array of Long values in Kotlin and a position parameter, when the position is in the middle of two of the array values, return the lowest

Spotify's lyrics API provides an Array of miliseconds to mark when the lyric line has changed. Having a Media Player that updates it's position every 50ms, how should i code in Kotlin the way to find the correct lyric line? The position param can be in the middle of two values of the array, so I want to get the lowest one of that two.
I tried to get the lowest value compared to the position parameter but lol, it will always be the first value of the Array... Silly fault of mine.
The problem is that I have a third one that indicates the range of that two value. For example: I have an Array of [45, 78, 125, 198]. If I pass the position param where it's value is 95, I want to return the 78 (that is the lowest value from itself, the position param and 125).
/** Input data for example (from your comment). */
val yourArray = arrayOf(45, 78, 125, 198)
val inputValue = 95
/** How to get needed index. */
val resultIndex = yourArray.indexOfLast { it < inputValue }.takeIf { it != -1 }
If you get resultIndex == null - it means you don't have value inside your array which lower then your inputValue.
I think it's simpler than find indexOfFirst and compare result later. And absolutely better and safer when sort() solution.
Insert the position param into the array, sort it, find its index and use it to get the closest value.
val array: MutableList<Long> = mutableListOf(4L, 9L, 5L, 1L)
val position = 7L
array.add(position)
println(array[array.sorted().indexOf(position) - 1])
Output: 5
If I correctly understand, you need simply use min function for compare two numbers in Kotlin and find the low one: link
Here’s a way to do it without having to make two copies of the list and doing a sort.
val foundIndex = lyricTimesList.indexOfFirst { it > inputTime }
val result = if (foundIndex == -1) lyricTimesList.size - 1 else foundIndex - 1
Note the result could be -1 if the input time is less than the first number in the list.

How do you call a variable created earlier in a when in kotlin?

I'm trying to list the first 100 of a shuffled list. I'm telling it to shuffle if the list is at 0 and then increment. I then am trying to call that list in another section of the when but it's not working. How can I accomplish this?
when (countF) {
0 -> {
//shuffle at 0
val randomChaos = chaosList.asSequence().shuffled().take(chaosList.count()).toList()
cResult.text = randomChaos.elementAt(countF) + countF + "\n\n\n\n\n\n\n\n" + this.cResult.text
countF++
}
1-99 -> {
//show 1-99
cResult.text = randomChaos.elementAt(countF) + countF + "\n\n\n\n\n\n\n\n" + this.cResult.text
countF++
}
100 -> countF = 0
You would need to create the val randomChaos before the when enclosure for it to be available in the scope of multiple branches of the when statement.
That said, the way you're getting a random element is very convoluted. take(chaosList.count()) is completely redundant. And since you don't use multiple sequence operators, creating a sequence is also redundant. Finally, you are only pulling a single item from the random list, so it's unnecessary to create a shuffled list in the first place. Using elementAt() on a shuffled list is no different than picking any element out of that shuffled list, or simply picking a random item out of a list that isn't shuffled at all.
Also, the first two branches of your when statement currently would produce exactly the same results so they can be merged.
Based on what you described, I'm guessing you had this when statement inside a loop that tries to run it 100 times so you can list all the items. For that to work, you would need to shuffle the list one time outside the loop, and then you could iterate its elements in the loop.
However, there are functions that can make it easier to do what you're suggesting. Here's an example:
val randomChaos = chaosList.shuffled()
cResult.text = randomChaos.asSequence()
.take(100)
.withIndex()
.joinToString("\n") { (i, value) ->
"$value-$i"
}
In this case, using a Sequence helps avoid creating an intermediate list to hold the first 100 values.
var randomChaos = chaosList.shuffled()
fun cShuf() { randomChaos = chaosList.shuffled() }
cRoll.setOnClickListener() {
cResult.movementMethod = ScrollingMovementMethod()
if (countF < 1) { cShuf() }
cResult.text = randomChaos.elementAt(countF) + "\n\n\n\n\n\n\n\n" + this.cResult.text
countF++
if (countF > 100) countF = 0
}
I have figured out how to use a function to generate a new shuffe of the list once I've hit > 100 shown.
My issue with making it a function was I was trying to use val variable in the function but the variable already existed so I didn't need to use val, just the name of the variable.

I need to separate the text from a string based on column names

I am working on OCR based Android app, getting this text as string from the attached image dynamically (getting the text in Horizontal Direction from the image)
Text from Image:
"Part Name Part Cost Engine Oil and Oil Filter Replacement Rs 10K Alf Filter Rs 4500 Cabin AC Micro Filter Rs 4000 Pollen Filter Rs 1200 - 1500 AC Disinfectant Rs 3000 Fuel Filter Rs 6000 - 8000 Spark Plug Set Replacement (Applicable in TFSI / Petrol Car Range) Rs 10K Body Wash, Basic Clean 8. Engine Degrease Rs 3000 Body Wax Polish Detailed Rs 7000 - 8000 Car interior Dry Clean with Genn Clean Rs 8000 - 10000 Wheel Alignment \u0026 Balancing Rs 6000 - 7000 Brake Pads Replacernent (Pair) Rs 30K - 32K Brake Disc Replacernent (Pair) Rs 30K - 35K ..........".
I need to separate the Part Name and Part Cost(just 2 columns i.e Part Name, Part Cost) (ignore all extra text from the column heading). Separate the values from String and should store it in SQLIte Database Android. I am stuck how to get the values and separate them.
The text returned from the OCR isn't ideal. The first thing you should do is check if whatever OCR solution can be configured to provide a better output. Ideally, you want the lines to be separated by newline characters and the space between the columns to be interpreted as something more useful, such as a tab character.
If you have no way of changing the text you get, you'll have to find some way of parsing it. You may want to look into using a parser, such as ANTLR to make this easier.
The following observations may help you to come up with a parsing strategy:
Column 2 items all start with "Rs" or "Upto Rs".
Column 2 items end with:
A number (where a number is allowed to be a string of digits [0-9.], optionally followed by a "K"
"Lakh"
Column 1 items don't begin with a number or "Lakh"
So a basic algorithm could be:
List<String> column1 = new ArrayList<String>();
List<String> column2 = new ArrayList<String>();
String[] tokens = ocrString.split(" ");
List<String> column = column1;
String item = "";
for (int i = 0; i < tokens.length; i++) {
String token = tokens[i];
String nextToken = i == tokens.length - 1 ? "" : tokens[i+1];
if (column == column1) {
if (token == "Rs" || (token == "Upto" && nextToken == "Rs")) {
column = column2;
column.add(item); item = "";
i--; continue;
}
item += " " + token;
} else {
item += " " + token;
if (/*token is number or "Lakh" and nextToken is not*/) {
column.add(item); item = "";
column = column1;
}
}
}

Using limit () and offset () in QueryBuilder (ANDROID , ORMLITE)

#SuppressWarnings("deprecation")
public List<Picture> returnLimitedList(int offset, int end) {
List<Picture> pictureList = new ArrayList<Picture>();
int startRow = offset;
int maxRows = end;
try {
QueryBuilder<Picture, Integer> queryBuilder = dao.queryBuilder();
queryBuilder.offset(startRow).limit(maxRows);
pictureList = dao.query(queryBuilder.prepare());
} catch (SQLException e) {
e.printStackTrace();
}
return pictureList;
}
I have a table of Pictures in the database, and must return a limited list, 20 lines at a time.
But when I use ex: QueryBuilder.offset(11).limit(30);
I can not return the list limited to 20 lines.
The list only comes to me with the limit.
It's as if the offset remain always with value 0
ex: (0 - 30)
Is there any other way to return a limited list for initial index and end index?
Could anyone help me?
This question was asked two months ago, but I'll answer if anyone stumbled on the same problem as I did.
There's misunderstanding about what offset means in this case, here follows what SQLite Documentations says about it
If an expression has an OFFSET clause, then the first M rows are omitted from the result set returned by the SELECT statement and the next N rows are returned, where M and N are the values that the OFFSET and LIMIT clauses evaluate to, respectively.
Source
Based on your query, you'll return 30 lines starting at the #11 line.
So the correct way is:
queryBuilder.offset(startRow).limit(20);
With limit being the number of rows that will return, not the ending row.
pictureList = dao.query(queryBuilder.prepare());
And the returned List with the first value starting on pictureList.get(0)
Edit: #Gray 's help on comments

Check Value from mysql using android

I have a table which has a field called Sold , in that field i store 0 and 1.
So 1 means sold and 0 mean Available.
K, my problem is that ,I want to change 0 to available and 1 to sold when i diplay information into my emulator , here what i tried but it returning sold even though i have 0 in my database :
if (sold.length()==0){
Log.d("checking","Inside = 0");
val = "Available";}
else if (sold.length()>0)
Log.d("checking","Inside = 1");
val = "Sold
And sold contains a value from a database.
please help to change 0 to Available and 1 to Sold.
if(sold.equalIgnoreCase("0"))
{
Log.d("checking","Inside = 0");
}else
{
Log.d("checking","Inside = 1");
}
The sold.length you have there is the actual length of the String representation "sold" that you are using. Meaning that if you have the word "some" this .length() equals 4.
So in your snippet the sold variable is the "0" or "1" respectively so the sold.length() always equals to 1. Try to cast the String variable into an Integer and make the comparison or even better try to make the variable into Integer from the beginning.

Categories

Resources