How do I create a list of non-repeating cells/numbers in Google Sheets? - google-sheets

I’m trying to emulate Minesweeper in Google Sheets, and for this I want to create a second map adjacent to the first with all of the correct values already in it. To randomize bomb position, I need a list of random numbers or cells(cells would be preferable). However, I cannot figure out how to do this without ending up repeating numbers. The result would ideally be a vertical array of cell coordinates. Thank you!

Answer
The following formula should produce the result you desire:
=SORTN(FLATTEN(MAKEARRAY(10,10,LAMBDA(row,col,ADDRESS(row,col)))),20,,RANDARRAY(100),)
In =MAKEARRAY, change the first 10 to adjust how many rows to randomly choose from, or the second 10 to adjust how many columns to choose from. The value in =RANDARRAY must be equal to the product of the number of rows and the number of columns. (e.g. in the above example, 10*10=100).
Change the 20 to adjust how many randomly chosen values to return.
Explanation
=MAKEARRAY is used to generate an array of every possible row and column combination. It accepts a =LAMBDA, which in this case is just the =ADDRESS function. The first two arguments of =MAKEARRAY determine how large the array should be, which is why changing them adjusts how many rows/columns to randomly pick from.
Then, the result of =MAKEARRAY is squashed into a single column using the =FLATTEN formula.
Finally, the entire thing is sorted randomly using =SORTN combined with =RANDARRAY. =SORTN also limits the number of results that are returned dependent on its second argument, which is why changing it adjusts how many results are returned.
If you want information on how to "freeze" the value of =RANDARRAY so it doesn't recalculate each time you change something, check out this question by player0.
Functions used:
=MAKEARRAY
=LAMBDA
=ADDRESS
=FLATTEN
=SORTN
=RANDARRAY

Related

I'm looking for a Google Sheet array function to add specific increments in each row

Here's how it looks.
Hi. I'll try to be as specific as possible about this :)
I'm making a Google Sheet page similarly to a game character progression, that will automatically fill up based on data from other sheets: specifically, these "EXP" bars fill up with values based on the hidden cell A5.
For instance, the cells from D5 to BA5 represent LV1.
As the function shows, every time the value in A5 reaches an even number (for LV1, it's all even numbers from 2 to 100), one of the slots will fill up with a number, that with conditional formatting, will change the color of the cell and make it look like an actual exp bar filling up.
I'm using the function =COUNTIF(A5,">=2") and manually changing it to ">=4", ">=6" etc until ">=100" for the first bar. The second bar will have its 50 cells with all even numbers from ">=102" to ">=200".
So, to get to the question: is there an array formula that facilitates the process of filling a great amount of these bars with functions, without changing them all manually? Even automating *some *of this process would be great. Besides it being tedious, it leaves a lot of room for human error.
Thank you in advance to whoever's got an answer.
P
I haven't tried much as at the moment I am very confused by the vast amount of options array formulas provide. I have 48h of experience in the matter.
I suggest you to use the values of columns and rows here as helpers to do calculations. If you know that every column adds 2 and every row adds 100 you can set a formula like this for the whole range (select the whole range and add just one rule of conditional formatting):
=$A$5>((Column(D5)-3)*2+(Row(D5)-5)*100))
You use $symbol to make A5 steady and D5 value will "move" to each cell for conditional formatting, so you don't have to make an ARRAYFORMULA for this

Google Sheets: How to make a stacked/aggregate chart

I have made a bar chart which aggregates my data, but is there any way I can split each bar based on the data it is aggregating - similar to how a stacked bar chart would look?
Here is a bad artists impression (thick blue lines mine). The idea is that it's important to know from looking at the graph if I sold 5 at £1, or 1 at £5.
Ideally this would work even if the price for each item is variable, but that is not essential (eg: if there is a 'hack' with hardcoding Apple = 3, I can live with that.)
I'm also fine inputting helper columns etc, within reason, but I would want to be able to easily continue to add things to the list on the left without having to add new helper columns each time (calculated ones are fine, of course.)
Thanks in advance.
UPDATE: With thanks to Kin Siang below, I ended up implementing a slightly modified version of their solution, which I am posting here for completeness.
I added a very large (but finite) number of helper columns to the right, with a formula in each cell which would look for the nth occurrence of the item in the main list (wrapped in an iferror to make the unused cells blank).
=iferror(index(FILTER($A:$B,$A:$A=$D2),E$1,2))
Theoretically it could run out of space one day, but I have made it suitably large that this should not be an issue. It has the advantage over the other solution that I do not need to sort or otherwise manipulate the input range and can continue trickling in data to the main list and have the chart automatically update.
Yes, it is possible to display the chart in your case, however need some data transpose in order to do so, let me show you the example with dataset
Assuming this is your original data:
First sort the data by alphabet, and enter this formula in new column
=if(G39="",1,if(G40=G39,I39+1,if(G40<>G39,1)))
Next add new column for categorical purpose, by using concatenate function
="Price"&I40
In the transform data for chart purpose, enter this formula to split all price into different row, different column for different product
=sumifs($H$40:$H$47,$G$40:$G$47,$A41,$J$40:$J$47,B$40)
After that i select stack bar chart and ensure the price in under series, in case in 23 will have some problem to set price at series correctly, you can use 33 data create stack bar chart and update the data range again, it will work also
Here is the cute chart you expected, accept if help :)
*When certain fruit has less price record, it is advised to fill in 0, as the data table need in same column (see the orange price 3), although I didnot test if blank

Counting the Number of Empty Cells between Non-Empty Cells in Google Sheets

I'm trying to count the number of empty cells that exist in a column between each non-empty cell but haven't been able to work out how.
Using this, I'm also trying to find the largest "empty distances" and locate the cell in the center of these distances.
The sheet I'm working with lists a set of marker colors and denotes the ones that are owned out of the full set of colors. I'm trying to find the largest ranges of missing colors and then find the colors in the middle of those ranges in order to find a handful of markers that would best help to fill out the spectrum.
Columns 1-6 are information- Column 7 marks whether the color is owned:
I may have an answer that helps you.
I could only get it to work using a helper column, but someone may know how to eliminate that requirement.
The helper column creates an array, basically listing the row numbers of the rows that have an "x" in your column B.
The main formula then measures the gap between each of these listed row numbers. It also checks the gap before the first "x", and after the last "x". Note that I have the data starting on row 2, which complicates the formula, but makes the sample sheet clearer - this can easily be changed to row 1 if you prefer.
={F2-1;
query(ArrayFormula(if(isnumber(F3:F),F3:F-F2:F-1,"")),
"select Col1 where Col1 > 0",0);
counta(A2:A)-indirect("F"&COUNTA(F$2:F))}
See a sample sheet here:
https://docs.google.com/spreadsheets/d/19QUFGRqTT6BqOsBrEBpTIxQCeNdRa5mzXhxQpCZ8sV4/edit?usp=sharing
Then I used a second formula to calculate the max gap between "x"s, (or before the first or after the last x).
Note that calculating the midpoint of the gaps, and doing a lookup of the corresponding mid-point colour, is something that can be added to this answer, if you share a sample copy of your sheet and share it for editing.
Let me know if this helps. I'll add more explanation to describe what the formula is doing tomorrow.
And I'll provide a second tab with the formulas adjusted to work with data beginning on row 1.
You can also get the lengths of the gaps using Frequency:
=ArrayFormula(frequency(if((B1:B20<>"X")*(A1:A20<>""),row(B1:B20)),if((B1:B20="X")*(A1:A20<>""),row(B1:B20))))
but finding the centres of the gaps and allowing for equal-sized gaps is more difficult.
This should find the position of the "X" at the end of the longest gap:
=ArrayFormula(
sum(frequency(if((B1:B20<>"X")*(A1:A20<>""),row(B1:B20)),
if((B1:B20="X")*(A1:A20<>""),row(B1:B20)))*(sequence(countif(B1:B20,"X")+1,1)<=
match(max(frequency(if((B1:B20<>"X")*(A1:A20<>""),row(B1:B20)),
if((B1:B20="X")*(A1:A20<>""),row(B1:B20)))),frequency(if((B1:B20<>"X")*(A1:A20<>""),row(B1:B20)),
if((B1:B20="X")*(A1:A20<>""),row(B1:B20))),0)))+
countif(sequence(countif(B1:B20,"X")+1,1),"<="&
match(max(frequency(if((B1:B20<>"X")*(A1:A20<>""),row(B1:B20)),
if((B1:B20="X")*(A1:A20<>""),row(B1:B20)))),frequency(if((B1:B20<>"X")*(A1:A20<>""),row(B1:B20)),
if((B1:B20="X")*(A1:A20<>""),row(B1:B20))),0))
)
and then it should just be a case of working backwards from there to the centre of the longest gap. However the formula needs further refinement to deal with the cases
(1) Where the longest gap is after the last "X"
(2) Where there is a tie for the longest gap
(3) Where there is a need to list the longest, second longest, third longest gap etc.

How to delete empty cells and shift up in Google Sheets?

Is there a way to delete empty cells in a given range and shift the column up to the desired display as shown below? Closest I came was
=ARRAYFORMULA({A1:C1; TRANSPOSE(SPLIT(TRANSPOSE(QUERY(A2:C,,999^99)), " "))})
which removes empty cells, but splits the first names and surnames into separate cells, which I have not figured out how to avoid. Pfa a made-up sample of current and desired displays:
Current Display
Desired Display
I'm new at this, but I came up with a bit of a brute force method, which may help you.
={
{(A1:C1)};
{FILTER(A2:A100,A2:A100<>"");indirect("N1:N" & 100-counta(A2:A100))},
{FILTER(B2:B100,B2:B100<>"");indirect("N1:N" & 100-counta(B2:B100))},
{FILTER(C2:C100,C2:C100<>"");indirect("N1:N" & 100-counta(C2:C100))}}
Assuming your data block is in columns A1:C100, this formula filters blank cells from each individual column, and then pads each column with blank cells at the bottom, to make the three arrays equal in length/dimension.
Note that in "100-counta(...", the 100 is the expected maximum length of your data column.
This could be calculated, and must be the same for all three columns.
Note also that the first array is horizontal (ends with a semi-colon), followed by the three columns, stacked beside each other (ends with a comma).
Here is a working example.
https://docs.google.com/spreadsheets/d/1MGaqqGrkmIliuAzEqxPtdEVZXWPN2K5W7jFFM-ZnwgE/edit?usp=sharing
If I missed something you were trying to achieve, let me know.
Also, I'm sure that there is a more elegant way to do this, or one not requiring the use of a block of "reserved" blank cells, but I couldn't think of that at the moment.
Edit: The formula as follows also works. But you need to remember to set the "100" value to be equal to the number of rows in your data block, since we pad the columns with the necessary number of blanks rows, after removing the blank cells in each column.
={
{(A1:C1)};
{FILTER(A2:A,A2:A<>"");indirect("N1:N" & 100-counta(A2:A))},
{FILTER(B2:B,B2:B<>"");indirect("N1:N" & 100-counta(B2:B))},
{FILTER(C2:C,C2:C<>"");indirect("N1:N" & 100-counta(C2:C))}}

How to average the last seven values in a column?

I am trying to find the average of the seven most recent entries in a row, as seen in
this
spreadsheet.
I found a few questions similar to mine, but I am still pretty confused on how the answers work. The questions similar to mine can be found on the left side of my spreadsheet.
I think that the formulas would work for me with a few simple adjustments of which values to use, but I can't seem to figure it out. I would really appreciate if someone could explain one of the existing answers or come up with another one that works.
The spreadsheet is updated daily, so I need something that would continue to work as more and more data is added to the column.
Try:
=round(AVERAGE(OFFSET(H1,MAX(ARRAYFORMULA(ROW(H:H)*--(H:H<>"")))-7,,7)))
here's working sample
Explanation
We are getting the last non empty row: MAX(ARRAYFORMULA(ROW(H:H)*--(H:H<>"")))
Then with offset formula we are getting the range of last 7 cells in a column.
And then just use AVERAGE.
More info
You may find more info about finding the last non empty row here:
Selecting the last value of a column
Another way is to use INDEX and MATCH. The first match finds the position of the last number in the range and takes 6 away from it: the second match finds the position of the last number in the range. Passing it through the INDEX function gives a reference that you can use to give a range of 7 cells for AVERAGE to work on.
=average(index(H:H,match(999,H:H)-6):index(H:H,match(999,H:H)))
So my answer is like your Link2
The big snag here is if you have a text cell in the range (like "Nothing") it is much more difficult to work out which cell to start from to get an average of 7 cells. I think I know how to do it in Excel using OFFSET but offset doesn't seem to work in the same way in Google Sheets.
However I can see there is a solution to this in your Link3 which should work for you if you change A:A to H:H and SUM to AVERAGE. I have tested it on the average of the last ten cells which includes a "Nothing" cell:
=ArrayFormula(AVERAGE(QUERY(SORT(H:H,ROW(H:H)*ISNUMBER(H:H),0),"select * limit 10")))
and it gives the correct answer 61.8.
The way array formulas work in general is that instead of passing a single value to a function you pass a whole range or array (a list of values) and the function processes them one by one. The above formula takes the whole column H:H and sorts it on the row numbers in descending order but those cells which don't contain a number give zero in the multiplication and are sorted to the bottom. Then the query takes the top (in my case) 10 cells and passes them to AVERAGE.
BTW this doesn't have to be declared as an array formula: this also works
=AVERAGE(QUERY(SORT(H:H,ROW(H:H)*ISNUMBER(H:H),0),"select * limit 10"))

Resources