In my last post about Excel and palindromes I showed you **how to check if a sentence is a palindrome**. Today, based on the list of different sentences (including palindromes) we will prepare an automatically generated list containing only palindromes.

We will use a helper column (column F in my case). Let’s select the amount of cells in our helper column to equal the number of sentences in our source list. In my case it’s 7 cells.

Next, let’s type in the following array formula:

{=SMALL(IF($D$8:$D$14,ROW($D$8:$D$14),""),ROW(INDIRECT("1:"&amp;amp;COUNT(IF($D$8:$D$14,ROW($D$8:$D$14),"")))))}

The formula uses the “is palindrome?” column ($D$8:$D$14) we talked about last time. For each found palindrome it returns the palindrome’s row number, and the error value #N/A otherwise.

What’s left now is to type the following formula into the first cell of my palindrome’s list:

=IF(ISERROR(INDIRECT(ADDRESS(F8,2))),"",INDIRECT(ADDRESS(F8,2)))

We could alternatively use a simpler formula:

=IF(ISERROR(F8),"",INDEX($B:$B,F8))

and copy it down to the cells below. Should work.

Marcin

]]>- So many dynamos!
- Rise to vote, sir
- No devil lived on
- Do geese see God
- Are we not drawn onward, we few, drawn onward to new era?

Let’s prepare a **formula** which **will return TRUE when the selected string is a palindrome**. In my next blog entry, based on the column with many text strings (including palindromes) we will also generate a **list containing ONLY palindromes**.

Our worksheet, before we type in any formulas, looks like this:

To check if a string is a palindrome I will loop through all the string’s letters (form left to right) and compare, in sequence, each letter with it’s “mirrored” equivalent. The first and the last letter have to be the same, the second and the second last has to be the same and so on.

If this condition is fulfilled it means that the string is a palindrome.

My first approach to the problem below (it’s an array formula what means that you enter it using SHITS+CTR+ENTER)

{=SUM((UPPER(MID(SUBSTITUTE(B8," ",""),ROW(INDIRECT("1:"&LEN(SUBSTITUTE(B8," ","")))),1))=UPPER(MID(SUBSTITUTE(B8," ",""),ABS(ROW(INDIRECT("1:"&LEN(SUBSTITUTE(B8," ",""))))-(LEN(SUBSTITUTE(B8," ",""))+1)),1)))*1)=LEN(SUBSTITUTE(B8," ",""))}

The cell B8 contains the string to be analyzed.

As I said earlier, the formula compares each letter from the beginning of the string with the symmetrically located letter from the end. If two letters are the same the formula returns TRUE for each comparison.

Each logic value is then multiplied by 1 (to get a number which could be used in mathematical calculations) .

Finally the formula adds up all the numbers. If the sum is equal to the length of the string it means that we have just discovered a palindrome. I hope that the picture below will help you a little in understanding what I am talking about.

To make the formula uninfluenced by the size of the letter (upper/lowercase) I used function **UPPER** to convert all letters into uppercase before I start comparing them. I have also removed all spaces from the string using function **SUBSTITUTE**.

First of all the formula doesn’t seem to work properly with strings containing periods and commas. You can see it on the picture below.

In all cases with single words or sentences without any punctuation the formula returns correct results. But in other cases (I marked it red) the output is wrong. The problem can be solved by removing from all strings not only spaces, but also commas and periods.

To do so, we will replace function

SUBSTITUTE(B8," ","")

used in the above formula with

SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(B8," ",""),",",""),".","")

The problem with this approach is that there are **too many nested formulas and Excel** (at least in version 2003) will return an error. We could think about a better algorithm, but since we already started with the method described we will trick Excel a little. Let’s create a defined name *“ourstring”* which will refer to the formula

=SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(B8," ",""),",",""),".","")

Now we will use this defined name in our formulas and Excel shouldn’t say “NO”. The results returned are now correct.

Right now, our formula slides through all letters (comparing them), from left to right and, at the same time, from right to left. As a result, the sentence characters are compared twice. When the formula starts it compares the first character with the last, and later after it reaches the end the last character is being compared again with the first one. It’s unnecessary.

So, let’s tweak our formula so it doesn’t do this extra work anymore:

={SUM((UPPER(MID(ourstring,ROW(INDIRECT("1:"&ROUND(LEN(ourstring)/2,0))),1))=UPPER(MID(ourstring,ABS(ROW(INDIRECT("1:"&ROUND(LEN(ourstring)/2,0)))-(LEN(ourstring)+1)),1)))*1)=ROUND(LEN(ourstring)/2,0)}

Have fun with palindromes!

Marcin

]]>… what I really want is to use the same color for each duplicated value. And at the same time, use a different color for different values. It might sound complicated, but is not and the animation below should settle all the doubts.

I also like the idea of having an option of easily changing the colors. To be honest, I don’t have a clue about how I could accomplish it with **conditional formatting** only. And since I don’t have a clue I decided to use **VBA**.

In the first step I prepared a list of colors I want to use. It’s nothing, but a range with colored cells (we simply use Excel formatting).

Every time our macro finds a duplicate value in a chosen column, it will get the color from one of previously colored cells (our color list mentioned above). Starting from the top one and moving down. When it reaches the end it will – surprise, surprise – start from the beginning.

Because I want my macro to run and refresh cell colors every time I change a value in a selected row I assigned the macro to Excel **onChange event**. And finally, my macro:

Private Sub Worksheet_Change(ByVal Target As Range) Dim rngKolory As Range Dim rngDoPokolorowania As Range Dim LicznikKolorow As Integer Dim Licznik As Integer Dim rngKolumna As Range Dim rngDaneWypelnione As Range ' cells with colors to choose from Set rngKolory = wksKolory.Range("rngKoloryStart").Resize(wksKolory.Range("settIleKolorow").Value, 1) ' cells with data to be "colored" Set rngDoPokolorowania = wksDane.Range(Range("rngDaneStart"), Cells(65535, Range("rngDaneStart").Column).End(xlUp)) ' column with data Set rngKolumna = Columns("B") With wksDane Set rngDaneWypelnione = .Range(.Range("rngDaneStart"), .Range("rngDaneStart").Offset(10000).End(xlUp)) End With If Not Intersect(Target, rngKolumna) Is Nothing Then Application.ScreenUpdating = False ' ' Let's clear the whole data area (set background color to default) rngDaneWypelnione.Resize(rngDaneWypelnione.Count + 1).Interior.ColorIndex = _ wksKolory.Range("rngDomyslneTlo").Interior.ColorIndex LicznikKolorow = 1 ' color counter reset With rngDoPokolorowania ' first cell If Application.WorksheetFunction.CountIf(rngDoPokolorowania, .Cells(1).Value) > 1 Then .Cells(1).Interior.ColorIndex = rngKolory.Cells(LicznikKolorow).Interior.ColorIndex LicznikKolorow = LicznikKolorow + 1 If LicznikKolorow > rngKolory.Count Then LicznikKolorow = 1 End If 'more than one cell If rngDaneWypelnione.Count > 1 Then ' for following cells For Licznik = 2 To .Count If Application.WorksheetFunction.CountIf(rngDoPokolorowania, _ .Cells(Licznik).Value) > 1 Then If Application.WorksheetFunction.CountIf(Range("rngDaneStart").Resize(Licznik - 1), .Cells(Licznik).Value) > 0 Then .Cells(Licznik).Interior.ColorIndex = _ rngDaneWypelnione.Find(what:=.Cells(Licznik).Value, after:=.Cells(Licznik), SearchDirection:=xlPrevious, lookat:=xlWhole).Interior.ColorIndex Else .Cells(Licznik).Interior.ColorIndex = rngKolory.Cells(LicznikKolorow).Interior.ColorIndex LicznikKolorow = LicznikKolorow + 1 If LicznikKolorow > rngKolory.Count Then LicznikKolorow = 1 End If End If Next Licznik End If End With Application.ScreenUpdating = True End If End Sub

This might not be the most optimal solution, but in simple cases will play it’s role just fine.

Marcin

]]>What we want at the end is to **generate a list of top 10 selling products**. To make it slightly more difficult we want this list to **automatically update **every time the number of products sold changes, and – just for fun – we **don’t want to use VBA macros**.

First, let’s sort (descending order) all the sale figures and choose 10 best ones. To do so I decided to use the function **LARGE** and an array formula entered into the range I7:I16 spanning over 10 Excel cells. Our array formula looks as follows:

=LARGE(Sheet1!C4:C19,ROW(INDIRECT("1:"&ROWS(Sheet1!C4:C19))))

Where C4:C19 is a range with our sold products amounts.

As a result we get a list of top 10 sales.

Now, the more difficult part. How to assign product names to the numbers ?

If we were sure that the numbers of sold products will never be the same (we don’t have any recurrent values) we could simply use **INDEX** and **MATCH** functions to match the appropriate product name to the number. Our formula could look like this:

=INDEX(Sheet1!B4:B15,MATCH(C6,Sheet1!C4:C15,0),1)

And it should work fine. But, if the sale amounts repeat, the above formula will return the same product name for each repeating number.

That is not what we want. That’s why I decided to use a little different approach. To get the first product name I used the formula:

=INDEX(Sheet1!B4:B19,MATCH(F6,Sheet1!C4:C19,0),1)

And for the following product names, an array formula

=INDIRECT("Sheet1!"&ADDRESS(SMALL(IF(Sheet1!$C$4:$C$19=F7,ROW(Sheet1!$C$4:$C$19),65536),COUNTIF($F$6:F7,F7)),2))

inserted into one cell and copied over the remaining 8 cells.

As you can see on the animation in the beginning of this post, the solution seems to work just fine. **Every change in the sold products amount, results automatically in an adjusting change on our TOP 10 list**. Enjoy!

Marcin

]]>As it’s usually the case with Excel, we can approach the problem from many sides. I set my mind on fitting everything into one formula and using Excel **text function MID()**, which in my example chooses and displays the correct row (other functions fulfill only an auxiliary role). My formula from the cell $D$11 looks like below:

=IF(OR(D11-1>LEN(B8)-LEN(SUBSTITUTE(B8,CHAR(10),"")),D11<1),"Given row number should be higher that 0 and lower then the total rows number",MID(B8,IF(D11=1,1,SEARCH("@",SUBSTITUTE(B8,CHAR(10),"@",D11-1))+1),IF(D11=LEN(B8)-LEN(SUBSTITUTE(B8,CHAR(10),""))+1,LEN(B8),SEARCH("@",SUBSTITUTE(B8,CHAR(10),"@",D11)))-IF(D11=1,0,SEARCH("@",SUBSTITUTE(B8,CHAR(10),"@",D11-1)))))

The cell $B$8, as you already noticed, contains our source material – text string divided into many separate rows by break a line character (inserted using the shortcut ALT+ENTER).

For all those who prefer **VBA code** and user defined functions, I have prepared an example of how such a function could look like. I used probably the easiest approach and took advantage of VBA **SPLIT() function**. The function has it’s flaws but in this simple case, should be just fine.

I named my UDF (User Defined Function) **GetTextRow**, and the function takes two parameters. It needs to know where (what cell) is the string we are going to use as a source data, and what row number we want to display. The result is shown on the animation below:

And my VBA code:

Function GetTextRow(WhereFrom As Range, _ RowNumber As Integer) Dim Temporary As Long Dim TemporaryArray As Variant ' First, lets check if the text in the pointed cell is divided into separate rows at all ' If this is not the case, we will display the whole text Temporary = InStr(WhereFrom.Value, Chr(10)) If Temporary = 0 Then GetTextRow = WhereFrom.Value ' return text from pointed cell Else ' lets also check if the row number the user provided is not too big. TemporaryArray = Split(WhereFrom.Value, Chr(10)) If RowNumber - 1 > UBound(TemporaryArray) Or _ RowNumber = 0 Then GetTextRow = "It is strongly recommended to think over the row number you used" Else ' if everything is all right the function returns (displays) the chosen row GetTextRow = TemporaryArray(RowNumber - 1) End If End If End Function

Marcin

]]>There is **no VBA** involved and the whole idea is based on the usage of **conditional formatting** only.

Before we start writing proper formulas controlling the way our chart is going to look, let’s prepare “the stage”. What we need to do is to prepare an area of 100 (10 rows and 10 columns), preferably square, excel cells (unless you have a better idea about how it could look like).

Our basic “stage” is now set and I am quite happy about cells sizes. The only thing is that everything looks a little boring. And we don’t want to be boring, do we? So let’s add some colors. I decided to set the background color to blue and add white cell borders. Finally, let’s turn the worksheet grid off. Our chart starts to slowly take a final shape.

Now, what we need to do is to set conditions for **Conditional Formatting**, which will turn individual cells on and off, depending on the helper cell (where we keep the amount of cells which need to be coloured).

Let’s select all 100 cells of our chart, go to **the Conditional Formatting dialog box** and create a new rule. My first approach to creating a proper rule is:

=OR(ROW($B$11)-ROW()<=INT($M$3/10)-1,AND(ROW($B$11)-ROW()=INT($M$3/10),COLUMN()-COLUMN($B$11)<($M$3/10-INT($M$3/10))*10))

Where $B$11 is the bottom left cell (corner) of our chart and in $M$3 we keep the percentage (number of squares we want to highlight). You will probably have to adjust it slightly to meet your own needs.

I decided that I want all the cells fulfilling my condition to be green.

After a few minutes I realized though, that it could be done with a simpler formula (formatting condition):

=((ROW($B$11)-ROW())*10+COLUMN()-COLUMN($B$11)+1)<=$M$3

As before, $B$11 is the bottom left cell (corner) of our chart and $M$3 is the cell with the number of squares we want to highlight.

Until now, our starting point is the bottom left corner of the chart. But what if we wanted to start from the bottom right corner? Or, even better, what if we would like to have a choice?

All we need to do is to add a list with our choices (to do so I used the Excel validation feature), and to change our formula slightly, which will now look like this:

=((IF($A$5,ROW($B$11),ROW($K$11))-ROW())*10+ABS(IF($A$5,0,COLUMN($K$11))-COLUMN())-IF($A$5,COLUMN($B$11),0)+1)<=$M$3

The cell A5 contains TRUE if “Left2Right” direction is chosen, and FALSE otherwise.

If you have any doubts please download the example file attached and play around with my formulas.

Marcin

]]>From time to time though, you might want to reverse this action, remove line breaks and go back to having only one line of text. There is quite a few ways to do so, one of them is to use **Excel find/replace dialog box**. Using this feature is really straightforward, the only problem is that not everyone knows **how to type in the line break symbol**. The trick here is to **press ALT and then enter 010 using the numeric keypad**.

As a result you will probably see a small blinking dot (at least that’s how it looks on my computer) in the “Find what:” field.

You can leave the “Replace with:” field empty (the line break will then be simply removed). Or alternatively enter, let’s say, a space there, to replace line break characters with spaces.

If you want to keep the original multi-line text untouched, and what you need is to have a copy of the text with removed break lines, you can use Excel function **clean** which removes all non-printable characters from a string. It seems to do the job

Marcin

]]>One of handy features of Excel is it’s ability to create

The general idea behind bubble charts is, that for each bubble you need to define it’s position (X and Y coordinates) and size. Simple! But, since a picture is worth a thousand words, below you can find a very simple data table and a bubble chart prepared using this data. I hope it explains any questions you might have.

The first step to add some life to our bubble chart is to prepare a table with source data. In my case, the data table looks like that:

All data is prepared for years 1999-2005 and for 6 countries. As you might notice (after a quick table analysis) what we have prepared for each case is (mentioned earlier) X, Y coordinates and size.

I have also decided that my “tail” with historical data will follow the chosen year** **data for a span of 4 years. Consequently, **you will see a maximum of 5 bubbles for each listed country**. You can change this number if you like to suit your needs.

Through the years we will move using a scrollbar inserted using the Form controls toolbar. In Excel 2007, you can find the Scroll Bar control under the Insert dropdown on the Developer tab.

The scrollbar is linked to one of the worksheet cells. I named this cell ** “PRZESUNIECIE”** (sorry for foreign and probably meaningless names but the text was originally prepared in polish. And of course it’s not the names that matter, what matters is how we use them.)

Because I have decided that the maximum number of bubbles is going to be 5, I am going to define 5 data series for my bubble chart.

The main series („Series1” on the picture above) which refers to the “active”, chosen year is defined as follows.

*ToShow_X*, *ToShow_Y* and *ToShow_Bubble* are properly defined dynamic ranges, so that every time we choose the year, the data used for the chart will automatically slide through the data table. Always pointing to the position and size of the bubble which should be displayed first.

Dynamic ranges are defined as follows:

* ToShow_X*

=OFFSET (Sheet1!$D$7;Przesuniecie*3;0;1;7)

*ToShow_Y*

=OFFSET(Sheet1!$D$7;Przesuniecie*3+1;0;1;7)

*ToShow_Bubble*

=OFFSET(Sheet1!$D$7;Przesuniecie*3+2;0;1;7)

As I have mentioned earlier ** „Przesuniecie”** is a defined name referring to the cell (A1) linked to the scrollbar.

The cell $D$7 (used in the above formulas) is the first cell of my data table. Please have a look at the picture below for clarity.

The remaining series (2-5) for the historical data (tail) are defined in a very similar way.

All dynamic ranges we refer to from the “Edit Series” window have the same structure. Let’s have a look at ranges used by “Series2” (the first bubble of our “tail”)

ToFollow_X1

=IF(Przesuniecie<1;Sheet1!$D$2:$I$2;OFFSET(Sheet1!$D$7;(Przesuniecie-1)*3;0;1;7))

Using the range $D$2:$I$2, we are trying to cheat Excel a little and persuade it to hide bubbles we don’t want (yet) to see on a screen. Picture below should unveil the mystery of our $D$2:$I$2 range

ToFollow_Y1

=IF(Przesuniecie<1;Sheet1!$D$3:$I$3;OFFSET(Sheet1!$D$7;(Przesuniecie-1)*3+1;0;1;7))

ToFollow_Bubble1

=IF(Przesuniecie<1;Sheet1!$D$4:$I$4;OFFSET(Sheet1!$D$7;(Przesuniecie-1)*3+2;0;1;7))

For all other ranges the formula structure is the same, with the only difference shown on the picture below.

And that’s pretty much it. With all dynamic ranges defined and linked to the graph series what we need to do is to format the graph a little, choose nice bubble colors for each series and …. Enjoy effect of our work.

Marcin

]]>