If you want to follow the steps here is the Excel file I used:
]]>The new features include:
Be aware that Insider Fast builds can still be a little bit unpolished and not everything may work perfectly. But if you try it out and provide feedback with the smiley icon, that feedback will go directly to the Excel team and will get their attention.
]]>
Once all people have returned their RSVPs, you can use Excel to organise the replies into a list with two columns: Name and Meal choice. Something like this:
The name of the table is MealOrders.
Now you need to work out how many meals of each type you need to order from your catering company.
You could build a pivot table, but since there is a fixed number of just seven meal choices, we can also just build a simple table with a formula. Place the seven meal choices into seven cells and use a Countif function. Turn that into a table by pressing Ctrl-T or click Insert > Table. (Tables are great for working with data!!)
Something like this: The blue table has the user data, the green table has the count per meal choice.
The formula to calculate how many meals to order (in the green table) is a simple Countif function, which you can see in the formula bar, but here it is if you want to copy it:
=COUNTIF(MealOrders[meal choice],[@[meal chosen]])
In plain words: In the table MealOrders (the blue table), look at the column meal choice and count how often the value of the column meal chosen from the current table (the green table) appears.
So far, so good. You know how many meals of each type to order to keep your GOT team happy, which is, as we all know, not easy at times.
Now the catering company delivers the food. You need to instruct your wait staff to deliver the right meal to the right person. Let’s say you have one waiter for each meal type. How can you work out who has ordered which meal, so you can give each of the wait staff a distinct list of people to deliver these meals to? One option is to sort the MealOrder table by the column meal choice. That will still result in quite a long list and require some scrolling to see all the data.
What if there were a way to list all names for kosher meals in one cell? If you’ve been using Excel for a while, you know that this may not be so easy, but, …. drum roll ….
Excel 2016 with an Office 365 subscription has a new function called TextJoin(). The syntax for TextJoin is
=Textjoin(Delimiter, IgnoreBlank, Range)
Delimiter is a text string. You can specify any delimiter you want, and the delimiter can be more than one character. So, if you want to separate list entries with a comma and a space, then use a comma and a space, like “, “.
IgnoreBlank will take TRUE or FALSE. If the source data has blank cells and you want to ignore these, select TRUE.
Range is the list of cells that holds the values that you want to join or concatenate. \
Here are two examples:
You see that you can use cell references (formula in cell A9, written out in cell A10) as well as Structured References (formula in cell A12, written out in cell A13) like table names and table columns in the TextJoin function. With the IgnoreBlank parameter set to TRUE, blanks don’t feature in the result.
Now, back to our hungry Game of Thrones crowd.
Using TextJoin, we can create a comma separated list of characters for each meal choice in a single cell. But it requires a little twist.
Let’s add a column next to the How many column. We need to feed (pardon the pun) the TextJoin function only those cells that have the meal choice listed in our meal chosen column of the same row.
As the Range parameter, we need to use an IF function. If the meal choice in the MealOrder table is the same text as the meal chosen column in the current row, then return the Name from the MealOrders table, otherwise, return a blank cell.
So the parameter for the Range looks like this:
IF(MealOrders[meal choice]=[@[meal chosen]],MealOrders[Name],"")
This is not a complete formula, just the parameter for the Range. This IF function will return a list (or array) of values, which the TextJoin function can then interpret. Let’s go back to the unsorted list of orders:
If we apply the IF function above to the meal choice column when looking at “regular”, then the IF function will return an array that starts like this:
{"","Robert","","Catelyn","","","Jorah","Petyr".......}
For every name that sits next to the “regular” meal choice, the list will show the name. For all other meal choices, the list will show a blank.
In the TextJoin function, we’re asking to ignore the blanks when we set the second parameter to TRUE, remember?
=Textjoin(Delimiter, IgnoreBlank, Range)
So that’s what we will exploit for the solution.
In the results table with the column for How many meals we need to order, we can add another column to show who these meals need to be delivered to in a nice, concise, comma separated list. Like this:
Here is the formula:
=TEXTJOIN(", ",TRUE,IF(MealOrders[meal choice]=[@[meal chosen]],MealOrders[Name],""))
Now, here comes the catch. You’ve been waiting for that, haven’t you? It was too good to be true, right?
The catch is that the nested IF function returns an array, i.e. multiple values, to the TextJoin function. This makes the whole formula in cell F3 a so-called array formula. In order to show all results properly, an array formula must be confirmed by holding down both the Ctrl and the Shift keys and then pressing Enter.
This special way of confirming a formula in a cell will enable the TextJoin function to check out all the values that the If function comes up with. If the formula is confirmed with just hitting Enter, then only the first value of the array (or list) served by the nested IF function will be looked at.
You can tell if a formula has been confirmed with Ctrl-Shift-Enter, because if it has, it will be wrapped in curly braces in the formula bar. Check the screenshot. Do you see the curly braces? They are easy to overlook but essential for an array formula to work.
Every time you edit an array formula, you have to remember to press Ctrl-Shift-Enter!!
If you forget that keystroke, all the formulas in the table will deliver wrong results. The wrath of Daenerys will be upon you and her dragons will engulf you in fire. To avoid that, you may want to download the sample file and check out how all this works.
]]>Something was wrong.
For the last two years, we’ve had a procedure in place that imports event data from System A to System B. Now the users reported that the time stamps from System A were off in System B. All of them. By 12 hours.
OK, my employer’s company is in New Zealand, which means that time zones, regions and locales are a big problem for software that can be very US centric. Therefore, the “12 hours off” pointed towards a possible locale issue. But we couldn’t really see what had changed in our environment, or when.
We run many different systems from many different vendors, most of them from the US (Looking at you, Microsoft). There’s often a lot of fine-tuning and workarounds involved, so things work in New Zealand. And when the vendor publishes an update, our workarounds may be superseded and become obsolete, or in the worst case, even revert the issue and turn against us. That’s why we suspected that some change in one of our systems was to blame.
But which change? And when did it happen?
Enter Power Query and Excel charting for some quick ad-hoc analysis.
We knew in December 2016 the imported dates were still fine. We knew by the end of March 2017 the imported dates were off by 12 hours. So, something must have happened between December and March.
There are hundreds of events every day, so just “downloading” a few months worth of data into Excel would probably have caused a major grind-to-a-halt of my machine. But with Power Query, things are different.
I created a connection to the system where the dates were showing “wrong”, which is an on-premise SharePoint 2010 list. With just a few clicks on commands in the ribbon of the Power Query editor, I specified that I wanted data after 1-Dec 2016, and divided the data for date and time into different columns.
With that data in my spreadsheet, I created an XY Scatter chart with the dates on the X axis and the time on the Y axis.
Our business is 24/7, but I know that most of our events are during the day, so I expected a pattern of data points between 6 am and 6 pm. And sure enough, that is what I saw when I filtered the data for December:
That looked pretty normal. Each event is a dot. Most events happen during the day.
I changed the filter to January, then to February. The pattern stayed the same. Then I changed the filter to March and saw this:
Gotcha! The pattern changed after 15-March.
Now we know WHEN the change happened and we can consult our change management system to find out more about the exact changes that were performed on our systems that day and take steps to fix the problem.
That’s what I call “data squinting”.
Sometimes you don’t need a perfect chart, as long as you can identify a pattern by just glancing sideways at the data.
Setting up the data with Power Query was a breeze.
Before Power Query, I would have had to create a view in SharePoint first, with all the trouble you have when a list has more than 5,000 items. Then, after exporting the view to Excel, I’d have to use Excel formulas to split date and time into two columns. With that many data points, I’d be looking at some calculation time.
With Power Query, I can quickly set up my filter parameters by clicking a few commands, then split out date and time into different columns and load the query.
Then set up a scatter chart and off we go data squinting.
Have you used Power Query for a quick ad-hoc analysis like that? Let me know what you found.
]]>
I’m excited to announce that I will be sharing my favourite Excel tips at CPA Australia’s newest conference, Unlock Excel, happening in Melbourne, Brisbane, Sydney and Auckland this May.
In conjunction with SumProduct, Unlock Excel is bringing together myself and six of my fellow Microsoft Excel Most Value Professionals (MVPs) to unlock the secrets, new spreadsheet features and tips on how to become an Excel master over two captivating days.
There will also be an opportunity for you to get the answers to your most challenging problems direct from the Microsoft’s Excel developers and to hear what the next version of Excel will include!
Tickets on sale now. See you at Unlock Excel.
]]>When a formula references another cell, that cell reference has a column letter and a row number, for example, A1 or CX216. If a formula with a cell reference is copied, you can control how that cell reference behaves. You can make it point to the exact same cell, or you can let it adjust when the formula is copied. To do that, you can use the $ signs in the cell reference. Here’s how they work.
The $sign in a cell reference locks it, so it won’t change when copied down or across. The $ sign can be applied to the column letter, the row number, or both.
The reference A1 copied down will become A2. Copied across, it will become B1. Both the column letter and the row number are relative and will change when the formula is copied.
The reference $A1 copied down will become $A2. Copied across, it will be $A1. The column letter is now fixed (absolute) and will not change when the formula is copied.
The reference A$1 copied down will be A$1. Copied across, it will be B$1. The row number absolute and will not change when the formula is copied.
The reference $A$1 copied down or across will be $A$1. Neither row number nor column letter in an absolute reference will change.
When you edit a formula you don’t have to type the $ signs manually. Often it is quicker to let Excel do that work. Place the cursor inside or just behind a cell reference and hit the function key F4 on your keyboard. Each press of this key will change the reference type. From relative to absolute to locked row to locked column.
Here is a sample file. Four different ways to write a formula that references cell A1. Each time, the formula has been copied down two rows and then across two columns. Note that the F4 function key only works if the file is open in Excel for desktop, not in Excel online in the browser.
]]>“OData: The feed’s metadata document appears to be invalid.”
Other SharePoint sites in the same site collection offered their lists just fine. I started a Fiddler trace to see what was going on, and it showed that there was a problem with the ListData service metadata file that has the URL “http://<SitePath>/_vti_bin/listdata.svc/$metadata”. I plugged that URL into the browser and here, too, instead of presenting the XML of the metadata, I saw this error “An error occurred while processing this request.”:
It took a lot of searching and head scratching until I finally worked it out. I came across a blog post on Technet: why would listdata.svc return an error that explains how invalid characters in a column name can cause problems for the list service. I’m religiously avoiding special characters when naming columns, but I thought I’d take a look through the site’s lists. I found a list with a name that started with a number. While numbers are not exactly special characters, there are places where they can cause problems. Excel’s range names cannot start with a number, either, for example.
I renamed the list to start with a letter and refreshed the metadata tab. Lo and behold!
The SharePoint internal name has not changed, but the XML contains the renamed version. It appears that a list name starting with a number breaks the metadata view of a list in SharePoint 2010.
After closing Excel / Power BI Desktop to clear each query cache, the query into that SharePoint site worked fine from both tools.
Problem solved.
PS: I tried the same in SharePoint 365 and could not reproduce the problem there. The metadata page opens fine, but the name of the list that starts with a number appears like “C_123MyList”.
]]>Short summary: The clock consists of five squares with different magnitudes: 5 – 3 – 2 – 1 and 1 again.
Add up the magnitudes for the red and the blue squares to arrive at the number of hours.
Add up the magnitudes for the green and the blue squares, multiply the result by 5 to arrive at the number of minutes.
Ignore white squares.
Minutes are rounded to the nearest 5. AM and PM don’t exist.
So, the time 9:25 looks like this:
Red and blue squares are magnitudes 5 + 3 + 1 = 9. That’s the hour.
Green and blue squares are magnitudes 3 + 2 = 5. Multiply by 5 = 25. That’s the minute.
Not everybody may be willing to pay for the hardware version of the clock, but even if you will get one, you will need some practice to get comfortable reading what it says. So here is your free Fibonacci Clock created with Microsoft Excel.
This is really great and a major advancement. Instead of just glancing at the clock and knowing instantly what time it is, you now need to spend upwards of 5 seconds, maybe up to minutes, to work out what the time is by adding up colour-coded squares. Yep. It’s for nerds. Geeks welcome.
All you need is a working version of Excel on your device. It will work on Excel for your computer (PC or Mac), Excel for iOS, or Android, and with Excel on-line, because it only uses conditional formatting and a few formulas. There’s no VBA code involved. Here’s a screenshot of the file on my iPhone:
Many time stamps can be represented with various combinations of the squares. I’ve used formulas and conditional formatting to arrive at a combination that has the correct time and not too many blue squares.
Download the attached file and play with the settings. You can use the system time and edit any cell to refresh the clock, or enter a time manually and practice how to read it.
]]>The Microsoft Help page was showing an example screenshot, where the formula and the result did not match up.
This was an oversight in the help documentation and will be fixed.
But it sparked a few questions, like:
With the last parameter set to 0 or FALSE, Vlookup will go through the list from the top, one cell after the other, and will compare each value in the first column to the lookup value. The list does not need to be sorted. When it finds a match, it returns the value from the specified column. For large lists, this can be rather slow.
Vlookup with the 1 or TRUE as the last parameter (omitting the parameter defaults to TRUE), works differently. Vlookup will now assume/expect that the list is sorted ascending in the first column. In order to find the lookup value, it does not inspect each cell, but cuts the list in half. If the value at the half point is a match — Bingo! If not, it compares the value at the half point with the lookup value and decides: If the lookup value is greater than the value of the half point, then continue with toe lower half of the list. Cut the lower half of the list in half and see. If the lookup value is smaller than the value of the half point, then continue with the upper half of the list. Cut the upper part of the list in half and see.
So Vlookup will start with 1/2 of the list, then reduce to 1/4 of the list, then to 1/8, then 1/16, etc. until it finds the match. If it gets down to only one value, but that value is not an exact match, this value will be treated as the match and the corresponding column value will be returned.
Are your ears smoking yet? Let’s look at an example.
Consider this table
The formula in A12 is =VLOOKUP(“h”,A2:B10,2).
Vlookup will split the table in half and will look at the last value of the top half. If there is an uneven number of rows, it will grab the next row, too. So Vlookup will consider this range:
The table has 9 rows, so Vlookup looks at the fifth row of the data table.
With text values, “greater” and “smaller” refer to the position in an alphabetic sort. So “a” is smaller than “c” and “k” is greater than “c”.
The value “i” is greater than the search value “h”. Therefore, the desired value is above Excel row 6. Vlookup will then split this upper part of the table in half. Since there are 5 rows involved, the split will be done at the third row of the range:
Vlookup looks at Excel cell A4. This value “e” is smaller than the lookup value. That means that the value we want is not in the table range that was just inspectedd, so we can forget that part of the table. Next Vlookup looks at the lower half of the range from the previous step.
Again, the last value in that range is “i” and greater than the lookup value, so let’s split this range in half and look at the last row of the top half:
This range consists of one row only. There’s nothing more to split. The value is not an exact match, but the return column will be returned, anyway.
Vlookup with TRUE as the last parameter will deliver the value that is “equal to or smaller than” the lookup value.
Coming back to the example with the names at the top of the post:
The formula used is =VLOOKUP(“Akers”,B2:D5,2)
The range is split in half, the last value in the top half is greater than the lookup value, so the lower half of the table will not even be considered for further inspection. The top two rows will be split in half and a value smaller than “Carido” will be expected in the last cell of that next split. But the last value in a split has “Weiler” as the value. That is not smaller than the lookup value “Akers”. Hence an error is returned.
If we add one row to the lookup table range, the first pass of Vlookup will split the table of 5 rows in half, rounding up to contain the top three rows in the first pass. Now we use the formula
=VLOOKUP(“Akers”,B2:D6,2)
The value in cell B4 is a perfect match, so the value in column C is returned. Bingo! We have a correct result for an unsorted table, without the fourth parameter!
Magic? No, it is pure coincidence. Omitting the last Vlookup parameter on an unordered list MAY return the correct result, but the chances of that are rather slim.
In this case, it is pure luck that the lookup value matches the last entry of the range that Vlookup considers in this pass.
If you use Vlookup without the fourth parameter, you must ensure that the lookup table is sorted by the first column. Otherwise, any result you get back is as reliable as getting the lotto numbers from a toddler.
It may work, but in most cases it won’t.
]]>Opening Excel on the iPad for the first time, I found the user interface clean and intuitive. After playing with it for a few minutes, here are my first impressions.
Here’s a screenshot of a file I created on my computer and then emailed to myself. I started off with this:
I added some data, created a table from that data and created a chart from the table. Added a shape, moved the data validation cells to a different position, changed numbers in the conditionally formatted ranges and added a new formula using named ranges and the =Formulatext(C13) to show the formula I added.
Here is what it looks like with the data validation drop down opened. The screen is overlaid with grey and the data validation box really stands out.
VBA and external data sources are not supported in this release. When opening a file that contains VBA or links to external sources, a friendly message appears that informs the user, but after that the file will open fine.
Overall, I am very impressed with this Excel experience for iPad. You would not want to create complex models from scratch using just iPad, but it is now possible to take your Excel files with you, store them on your iPad and work on them off-line, doing more than just simple data edits.
Addendum Sunday 30th March 2014: This review is a very quick recap of how Excel for iPad deals with some of the most frequently used Excel functions. Bill Jelen, aka MrExcel has a review inspecting much more comprehensive scenarios here.
]]>I’ve posted my slide deck and all the files I demoed or used in videos in my OneDrive here: http://bit.ly/PZOrZm
Darrell took a few photos. I’ve pinched these:
Enjoy.
]]>PirateEric outlined a possible solution: Use a SharePoint list to save the user name when the button is clicked. When the page loads, look up the user in the list. If they already exist, redirect the page, if not, show the splash page with the button to accept the policy. If the policy changes and users need to be made aware of that, simply remove all items in the list that tracks the users. All this can be done with jQuery and web services.
That intrigued me and I had a go at actually building this, using Marc Anderson’s SPServices.
Create a SharePoint custom list with two fields, Title and UserName. The former is the out of the box field, the latter is a simple text field.
Create two pages, the Splash page with the button and the page that is the desired destination page for all visitors. In my sample these are called Splash.aspx and MainContent.aspx
On the Splash page the code that you can see below will be loaded before any other web part. If you use a Content Editor Web Part to load the code with a content link, make sure that it’s the first web part on the page.
In many cases, JavaScript and jQuery will be placed in the last web part of the page and run after the DOM has loaded. But in this case this would mean that the Splash page appears briefly, even if it is followed by a redirect to a different page.
The Splash page provides the policy (or terms and conditions) that the user must accept, and a link or a button that the user can click to accept. This link or button must then trigger a JavaScript function. That is very easy to do. I used a button and put the html straight into a CEWP like this:
<button onclick="PolicyButtonClick()" type="submit"> I accept the policy </button>
So the user clicks the button and the JavaScript function PolicyButtonClick() will run. This function can be found in the code below.
First, the jQuery library and the SPServices library are loaded. Then the user name of the current user is retrieved with the SPServices call using SPGetCurrentUser. It returns a text string in the format DOMAINAccount. Next, the SPServices call uses the GetListItem. In the call, a CAML query is constructed that will return only list items where the column UserName equals the current user. The items returned by that query are then counted. Since the user account is a unique value, we can safely assume that the query will either return one result or no result at all.
If the query returned an item, this means that the user has previously accepted the policy and the script will redirect to the MainContent.aspx page. If the query did not return anything, the current page will continue to be displayed.
When the user clicks the button to accept the policy, the user name is written into a variable. Then the SPServices operation to UpdateListItem is called and will create a new item in the list “PolicyAccepted”, storing the previously established account name in the column UserName. Then the MainContent.aspx page is loaded.
The next time the user opens the Splash page, their account name will be found in the PolicyAccepted list and they will not see the Splash page again, unless the entry in the list is deleted.
Here is the complete script:
<a href="/path/jquery-1.10.2.min.js">/path/jquery-1.10.2.min.js</a> <a href="/path/jquery.SPServices-2013.02a.min.js">/path/jquery.SPServices-2013.02a.min.js</a> // start the code even before the DOM is loaded, so not waiting for document ready //$(document).ready( function() { // get the user name var userName= getUserName(); // find the user name in the list var userAccepted = matchUserName(userName); if (userAccepted == 1 ) { // redirecting page window.location.replace("http://path/Documents/MainContent.aspx"); } //}); function getUserName() { var thisUserAccount= $().SPServices.SPGetCurrentUser({ fieldName: "Name", debug: false }); return(thisUserAccount); } function createNewItem(theTitle, theUser) { $().SPServices({ operation: "UpdateListItems", async: false, batchCmd: "New", listName: "PolicyAccepted", valuepairs: [["Title", theTitle], ["UserName", theUser]], completefunc: function(xData, Status) { } }); } function matchUserName(userName) { var queryText = "" + userName + ""; $().SPServices({ operation: "GetListItems", listName: "PolicyAccepted", async: false, CAMLQuery: queryText, completefunc: function (xData, status) { itemCount = $(xData.responseXML.xml).find("rs\:data, data").attr("ItemCount"); } }); return(itemCount); } function PolicyButtonClick() { var userName= getUserName(); var theTitle= "Accepted"; createNewItem(theTitle, userName); window.location.href = "http://path/Documents/MainContent.aspx"; }
Enjoy.
]]>