├── README.md ├── SetUDFs.bas ├── Sets.xlsm ├── SetsRunner.bas ├── clsKeyValueMap.cls ├── clsSet.cls ├── clsTestSet.cls ├── clsTranslateDna.cls ├── misc_functions.bas ├── modCopySheetsToNewWorkbook.bas ├── modTest_clsSets.bas ├── modTest_clsTranslateDna.bas ├── name_ranges.bas └── transcript.md /README.md: -------------------------------------------------------------------------------- 1 | # Excel VBA 2 | 3 | I still occasionally use Excel VBA although I now try to use the Excel formula language and LAMBDAs if possible. 4 | However, sometimes it is easier and cleaner to implement something in VBA. 5 | 6 | ## Set Operations - Implementing them in VBA 😬 7 | 8 | Something I find painful in Excel is the absence of _set_ logic. In SQL we have operators such as UNION, EXCEPT, and INTERSECT and they are super useful. 9 | I have seen implementations of these in Excel using combinations of FILTER, MATCH and so on in LAMBDAs. They implementations might be clever 10 | but they are not clear or intuitive to me. 11 | 12 | As a challenge, I decided to try to implement them in VBA using dictionaries. The implementations are not yet complete but I am making progress. 13 | Once I have finished, I will post an extensive blog on the subject. 14 | 15 | ### Guide to the files 16 | 17 | I have uploaded two __.bas_ module files and two _.cls_ class files. 18 | 19 | 1. __clsSet.cls__: This contains the set logic code and is the key file. 20 | 1. __clsTestSet__: Testing code for the main class file. Trying to implement a poor man's unit testing in VBA 🤪.* 21 | 1. __modTest_clsSets__: Creates the class instance for testing and calls the test routines. 22 | 1. __SetUDFs.bas__: User-defined functions that use the main class and that can be called in Excel. 23 | 24 | \* Before you ask, yes, I have tried RubberDuck but it is not working for me. Might be because I do my VBA work on a locked down work laptop 🤷‍♂️ 25 | 26 | 27 | I have also uploaded the macro-enabled Excel file and it contains all the code, name __Sets.xlsm__. 28 | ### TODO - lots! 29 | 30 | - Not yet finished! __⚠️__ 31 | - The code is not yet fully implemented or documented. 32 | - I will write a longer explanation of how this works in a blog once I have finished the coding. 33 | 34 | 35 | ## Blogging ℹ️ 36 | 37 | [Now blogging here](https://rotifer.github.io/) 38 | 39 | 40 | -------------------------------------------------------------------------------- /SetUDFs.bas: -------------------------------------------------------------------------------- 1 | Attribute VB_Name = "SetUDFs" 2 | Option Explicit 3 | ' Select Tools->References from the Visual Basic menu. 4 | ' Check box beside "Microsoft Scripting Runtime" in the list. 5 | 6 | 7 | ' Public function that can be called as a UDF. 8 | ' One glitch: The tab is disappearing (??) 9 | ' TODO: fix the tab 10 | Public Function UNION(rng1 As Range, rng2 As Range) As Variant 11 | Dim set1 As clsSet: Set set1 = New clsSet 12 | Dim set2 As clsSet: Set set2 = New clsSet 13 | Dim unionDict As Dictionary 14 | Dim elements As Variant 15 | set1.InputRng = rng1 16 | set2.InputRng = rng2 17 | Set unionDict = set1.UNION(set2) 18 | UNION = Application.WorksheetFunction.Transpose(unionDict.Keys) 19 | End Function 20 | 21 | -------------------------------------------------------------------------------- /Sets.xlsm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rotifer/VBA/346f8f0f6943ffd0eff03a95a4abcb73a0018b7d/Sets.xlsm -------------------------------------------------------------------------------- /SetsRunner.bas: -------------------------------------------------------------------------------- 1 | Attribute VB_Name = "SetsRunner" 2 | Option Explicit 3 | ' Assumes the "Microsoft Scripting Runtime" reference has been added. 4 | ' HOWTO: Select Tools->References from the Visual Basic menu. 5 | ' Check box beside "Microsoft Scripting Runtime" in the list. 6 | 7 | ' This module contains a set of subroutines the use the methos of clsSet 8 | ' to perform set operations (UNION, INTERSECTION and DIFFERENCE) on values 9 | ' extracted from pairs of ranges. The sets are implemented in the class as 10 | ' dictionaries with the range values for each set as dictionary keys where the 11 | ' values of each row are stored as tab-separated strings of the row cell values 12 | ' for each range. 13 | 14 | ' I call the clsSet methods in UDFs that can be called directly in Excel formulas but 15 | ' haven't been able to get that to work. 16 | 17 | ' ################################### subroutines ################################# 18 | 19 | ' Given two ranges and a target cell for first output, write out 20 | ' the UNION result. 21 | Sub Union(rng1 As Range, rng2 As Range, startCell As Range) 22 | Dim set1 As clsSet: Set set1 = New clsSet 23 | Dim set2 As clsSet: Set set2 = New clsSet 24 | Dim unionDict As Dictionary 25 | 26 | set1.InputRng = rng1 27 | set2.InputRng = rng2 28 | Set unionDict = set1.Union(set2) 29 | Call PrintSet(ActiveCell, unionDict, vbTab) 30 | End Sub 31 | ' Runner for UNION. 32 | Sub CallUnion() 33 | Dim rng1 As Range, rng2 As Range 34 | Set rng1 = Worksheets("Set1").Range("A1:B10") 35 | Set rng2 = Worksheets("Set2").Range("A1:B10") 36 | Call Union(rng1, rng2, ActiveCell) 37 | End Sub 38 | 39 | ' Print the split result of a concatenated keys of a dictionary (our Set), 40 | ' beginning at the start cell using the split character argument to generate the output values 41 | Sub PrintSet(startCell As Range, dictToPrint As Dictionary, splitChar As String) 42 | Dim i As Integer, j As Integer 43 | Dim key As Variant 44 | Dim arrRow() As String 45 | Dim cellVal As Va.riant 46 | i = 0 47 | j = 0 48 | For Each key In dictToPrint.Keys() 49 | arrRow = Split(key, splitChar) 50 | For j = 0 To UBound(arrRow) 51 | cellVal = arrRow(j) 52 | startCell.Offset(i, j).Value = cellVal 53 | Next j 54 | i = i + 1 55 | Next key 56 | End Sub 57 | 58 | ' Given two ranges and a target cell for first output, write out 59 | ' the INTERSECTION result. 60 | Public Sub Intersection(rng1 As Range, rng2 As Range, startCell As Range) 61 | Dim set1 As clsSet: Set set1 = New clsSet 62 | Dim set2 As clsSet: Set set2 = New clsSet 63 | Dim intersectionDict As Dictionary 64 | 65 | set1.InputRng = rng1 66 | set2.InputRng = rng2 67 | Set intersectionDict = set1.Intersection(set2) 68 | Call PrintSet(ActiveCell, intersectionDict, vbTab) 69 | End Sub 70 | 71 | ' Runner for INTERSECTION. 72 | Sub CallIntersection() 73 | Dim rng1 As Range, rng2 As Range 74 | Set rng1 = Worksheets("Set1").Range("A1:B10") 75 | Set rng2 = Worksheets("Set2").Range("A1:B10") 76 | Call Intersection(rng1, rng2, ActiveCell) 77 | End Sub 78 | 79 | ' Given two ranges and a target cell for first output, write out 80 | ' the DIFFERENCE result. 81 | Sub Difference(rng1 As Range, rng2 As Range, startCell As Range) 82 | Dim set1 As clsSet: Set set1 = New clsSet 83 | Dim set2 As clsSet: Set set2 = New clsSet 84 | Dim differenceDict As Dictionary 85 | 86 | set1.InputRng = rng1 87 | set2.InputRng = rng2 88 | Set differenceDict = set2.Difference(set1) 89 | Call PrintSet(ActiveCell, differenceDict, vbTab) 90 | End Sub 91 | 92 | ' Runner for DIFFERENCE. 93 | Sub CallDifference() 94 | Dim rng1 As Range, rng2 As Range 95 | Set rng1 = Worksheets("Set1").Range("A1:B10") 96 | Set rng2 = Worksheets("Set2").Range("A1:B10") 97 | Call Difference(rng1, rng2, ActiveCell) 98 | End Sub 99 | -------------------------------------------------------------------------------- /clsKeyValueMap.cls: -------------------------------------------------------------------------------- 1 | VERSION 1.0 CLASS 2 | BEGIN 3 | MultiUse = -1 'True 4 | END 5 | Attribute VB_Name = "clsKeyValueMap" 6 | Attribute VB_GlobalNameSpace = False 7 | Attribute VB_Creatable = True 8 | Attribute VB_PredeclaredId = False 9 | Attribute VB_Exposed = False 10 | Option Explicit 11 | ' SUMMARY: 12 | 'A simple example of object-oriented programming in VBA 13 | 'Provides a class to implement a simple dictionary where uninque string keys map to string values. 14 | 'Recent Excel versions on Windows provide a Dictionary object. 15 | 'This class is NOT TO BE USED when running recent versions of Excel on Windows. Use the provided Dictionary object instead! 16 | 'See Microsoft article: http://support.microsoft.com/kb/187234 17 | 'CODE NOTES: 18 | 'Internally the class uses a Collection and stores the key-value pairs as two-element string arrays. 19 | 'Its underlying Collection basis hidden from calling code and its public methods and properties 20 | 'try to make it look like a dictionary/hash. 21 | 'Both keys and values are simple string scalars so keys cannot map to arrays. Workarounds are possible where the values can be 22 | 'concatenated and then split into arrays when retrieved. 23 | 'If a key-pair is added to the collection for a key that already exists in the collection, the old entry is over-written. 24 | 'If different behaviour is required based on different values that have the same key, the calling code needs to deal with that. 25 | 'The keys are CASE-SENSITIVE ("a" != "A") 26 | 'EXAMPLE USAGE: 27 | ' Option Explicit 28 | ' Sub test_clsKeyValueMap() 29 | ' Dim dict As clsKeyValueMap 30 | ' Set dict = New clsKeyValueMap 31 | ' Dim Keys() As String 32 | ' Dim Values() As String 33 | ' Dim i As Long 34 | ' 35 | ' 'Add values to the dict object 36 | ' Call dict.addKeyValuePair("A", 1) 37 | ' Call dict.addKeyValuePair("a", 2) 38 | ' Call dict.addKeyValuePair("B", 3) 39 | ' Call dict.addKeyValuePair("A", 4) 40 | ' 41 | ' 'Test methods and properties 42 | ' MsgBox dict.KeyValueMapCount 43 | ' MsgBox dict.HasKey("A") 44 | ' MsgBox dict.HasKey("a") 45 | ' MsgBox dict.HasKey("B") 46 | ' MsgBox dict.ValueForKey("B") 47 | ' Keys = dict.Keys 48 | ' Values = dict.Values 49 | ' MsgBox Join(Keys, ", ") 50 | ' MsgBox Join(Values, ", ") 51 | ' 52 | ' 53 | ' 'Loop over the dict object printing keys and their values to the immediate window. 54 | ' For i = 0 To UBound(Keys) 55 | ' Debug.Print Keys(i), " => ", dict.ValueForKey(Keys(i)) 56 | ' Next i 57 | ' 'Output: 58 | ' 'a => 2 59 | ' 'B => 3 60 | ' 'A => 4 61 | ' End Sub 62 | 'TO DO: 63 | 'Allow more complex data structures, dictionaries of arrays, dictionaries of dictionaries etc. 64 | 'TO USE: 65 | 'Import the class module to project 66 | 'Create a normal module and paste in the test code given above in the sub named "test_clsKeyValueMap()" to ensure it is running 67 | 'AUTHOR: 68 | 'mick@javascript-spreadsheet-programming.com 69 | 70 | Private keyValueMap As Collection 71 | Private Const CODEVERSION As String = "TEST" 72 | 'Constructor: Initialize an empty collection 73 | Private Sub Class_Initialize() 74 | Set keyValueMap = New Collection 75 | End Sub 76 | 'Return the number of elements in the collection instance 77 | Public Property Get KeyValueMapCount() 78 | KeyValueMapCount = keyValueMap.Count 79 | End Property 80 | 'A flag to providing the code version of the class 81 | Public Property Get Version() As String 82 | Version = CODEVERSION 83 | End Property 84 | 'Add a two element string array to the collection where the first element acts as the key and the second as the value 85 | 'If the key does not already exist in the collection add the pair, 86 | 'else the key already exists, remove the old key-value pair and add the new one. 87 | Public Sub addKeyValuePair(key As String, value As String) 88 | Dim pair(1) As String 89 | 90 | pair(0) = key 91 | pair(1) = value 92 | If IndexForKey(key) < 0 Then 93 | keyValueMap.Add (pair) 94 | Else 95 | keyValueMap.Remove (IndexForKey(key)) 96 | keyValueMap.Add (pair) 97 | End If 98 | 99 | End Sub 100 | 'Given a key string, check the first elment of the array in each collection item for the key value. 101 | 'If a match is found, return the index where the match is found, else if not found return -1. 102 | 'This is a private property because the underlying collection is to be treated as unordered. 103 | Private Property Get IndexForKey(key As String) As Long 104 | Dim keyValuePair() As String 105 | Dim i As Long 106 | 107 | IndexForKey = -1 108 | If KeyValueMapCount > 0 Then 109 | For i = 1 To KeyValueMapCount 110 | keyValuePair = keyValueMap(i) 111 | If keyValuePair(0) = key Then 112 | IndexForKey = i 113 | Exit Property 114 | End If 115 | Next i 116 | End If 117 | End Property 118 | 'Return true if the given key is present in the collection 119 | 'Can be used by calling code to determine what to do if it needs to add a key-value pair where the key already exists. 120 | Public Property Get HasKey(key As String) As Boolean 121 | HasKey = False 122 | If KeyValueMapCount > 0 And IndexForKey(key) > 0 Then 123 | HasKey = True 124 | End If 125 | End Property 126 | 'Given a key check the collection for a match and return the matching value it maps to. 127 | 'If the key is not found, return a VB null string. 128 | Public Property Get ValueForKey(key As String) As Variant 129 | Dim pair() As String 130 | 131 | If KeyValueMapCount > 0 And IndexForKey(key) > 0 Then 132 | pair = keyValueMap.item(IndexForKey(key)) 133 | ValueForKey = pair(1) 134 | Else 135 | ValueForKey = vbNullString 136 | End If 137 | 138 | End Property 139 | 'Return a string array of all the key values i.e. the first elements in the two-element array items. 140 | 'If the collection is empty, return an array of one element where that element is a VB null string. 141 | 'The keys array elements are in random order because they were retrieved using a For Each to loop over the collection 142 | Public Property Get Keys() As String() 143 | Dim arrKeys() As String 144 | Dim i As Long: i = 0 145 | Dim item As Variant 146 | 147 | If KeyValueMapCount > 0 Then 148 | For Each item In keyValueMap 149 | ReDim Preserve arrKeys(i) 150 | arrKeys(i) = item(0) 151 | i = i + 1 152 | Next item 153 | Else 154 | ReDim Preserve arrKeys(i) 155 | arrKeys(i) = vbNullString 156 | End If 157 | 158 | Keys = arrKeys 159 | 160 | End Property 161 | 'Return a string array of all the keyed values i.e. the second elements in the two-element array items. 162 | 'If the collection is empty, return an array of one element where that element is a VB null string. 163 | 'The values array elements are in random order because they were retrieved using a For Each to loop over the collection 164 | Public Property Get Values() As String() 165 | Dim arrValues() As String 166 | Dim i As Long: i = 0 167 | Dim item As Variant 168 | 169 | If KeyValueMapCount > 0 Then 170 | For Each item In keyValueMap 171 | ReDim Preserve arrValues(i) 172 | arrValues(i) = item(1) 173 | i = i + 1 174 | Next item 175 | Else 176 | ReDim Preserve arrValues(i) 177 | arrValues(i) = vbNullString 178 | End If 179 | 180 | Values = arrValues 181 | 182 | End Property 183 | 'Use the given a key to locate a matching item in the collection. 184 | 'If the key is found, remove that key-value pair from the collection and return "True" to indicate successful removal. 185 | 'If the key is not found, return "False". 186 | Public Function DeleteKeyValuePair(key As String) As Boolean 187 | If KeyValueMapCount > 0 And IndexForKey(key) > 0 Then 188 | keyValueMap.Remove (IndexForKey(key)) 189 | DeleteKeyValuePair = True 190 | Else 191 | DeleteKeyValuePair = False 192 | End If 193 | End Function 194 | -------------------------------------------------------------------------------- /clsSet.cls: -------------------------------------------------------------------------------- 1 | VERSION 1.0 CLASS 2 | BEGIN 3 | MultiUse = -1 'True 4 | END 5 | Attribute VB_Name = "clsSet" 6 | Attribute VB_GlobalNameSpace = False 7 | Attribute VB_Creatable = False 8 | Attribute VB_PredeclaredId = False 9 | Attribute VB_Exposed = False 10 | Option Explicit 11 | 12 | Private dictSet As Dictionary 13 | Private m_inputRng As Range 14 | 'Constructor: Initialize an empty dictionary 15 | Private Sub Class_Initialize() 16 | Set dictSet = New Dictionary 17 | End Sub 18 | Public Property Let InputRng(rng As Range) 19 | Set m_inputRng = rng 20 | End Property 21 | 22 | ' Process the input range by generating an array where each array element is tab-separated string of 23 | ' the cell values for one row of the input range. 24 | Public Function rngValuesArray() As String() 25 | Dim row_ As Range 26 | Dim col As Range 27 | Dim cellValue As Variant 28 | Dim rngValues() As String 29 | Dim rowValues() As String 30 | Dim arrRowIdx As Integer: arrRowIdx = 0 31 | Dim arrRngIdx As Integer: arrRngIdx = 0 32 | For Each row_ In m_inputRng.Rows 33 | For Each col In row_.Columns 34 | cellValue = col.Value 35 | ReDim Preserve rowValues(arrRowIdx) 36 | rowValues(arrRowIdx) = cellValue 37 | arrRowIdx = arrRowIdx + 1 38 | Next col 39 | arrRowIdx = 0 40 | ReDim Preserve rngValues(arrRngIdx) 41 | rngValues(arrRngIdx) = Join(rowValues, vbTab) 42 | arrRngIdx = arrRngIdx + 1 43 | Next row_ 44 | rngValuesArray = rngValues 45 | End Function 46 | ' Create a set (dictionary keys) for the array generated by "rngValuesArray" 47 | Public Property Get rowsAsSet() As Dictionary 48 | Dim idx As Integer 49 | Dim rowValue As String 50 | Dim valuesArray() As String: valuesArray = Me.rngValuesArray 51 | For idx = 0 To UBound(Me.rngValuesArray) 52 | rowValue = valuesArray(idx) 53 | If Not dictSet.Exists(rowValue) Then 54 | dictSet.Add key:=rowValue, Item:=1 55 | End If 56 | Next idx 57 | Set rowsAsSet = dictSet 58 | End Property 59 | 60 | 61 | 'function isSuperset(set, subset) { 62 | ' for (const elem of subset) { 63 | ' if (!set.has(elem)) { 64 | ' return false; 65 | ' } 66 | ' } 67 | ' return true; 68 | '} 69 | 70 | Public Function IsSuperset(otherSet As clsSet) As Boolean 71 | Dim otherSetValues As Dictionary: Set otherSetValues = otherSet.rowsAsSet 72 | Dim thisSetValues As Dictionary: Set thisSetValues = Me.rowsAsSet 73 | Dim element As Variant 74 | For Each element In otherSetValues.Keys 75 | If Not thisSetValues.Exists(element) Then 76 | IsSuperset = False 77 | Exit Function 78 | End If 79 | Next element 80 | IsSuperset = True 81 | End Function 82 | 83 | ' Make a full copy of the dictionary (clone it). This is important to avoid 84 | ' unintended data mutations. 85 | Private Function CopyDict(ByVal dict As Dictionary) As Dictionary 86 | Dim ky As Variant 87 | Dim dictCopy As Dictionary: Set dictCopy = New Dictionary 88 | For Each ky In dict.Keys() 89 | dictCopy.Add key:=ky, Item:=dict(ky) 90 | Next ky 91 | Set CopyDict = dictCopy 92 | End Function 93 | 94 | Public Function UNION(otherSet As clsSet) As Dictionary 95 | Dim otherSetValues As Dictionary: Set otherSetValues = CopyDict(otherSet.rowsAsSet) 96 | Dim thisSetValues As Dictionary: Set thisSetValues = CopyDict(Me.rowsAsSet) 97 | Dim element As Variant 98 | For Each element In otherSetValues.Keys 99 | If Not thisSetValues.Exists(element) Then 100 | thisSetValues.Add key:=element, Item:=1 101 | End If 102 | Next element 103 | Set UNION = thisSetValues 104 | End Function 105 | 106 | Public Function Intersection(otherSet As clsSet) As Dictionary 107 | Dim otherSetValues As Dictionary: Set otherSetValues = otherSet.rowsAsSet 108 | Dim thisSetValues As Dictionary: Set thisSetValues = Me.rowsAsSet 109 | Dim intersection_ As Dictionary: Set intersection_ = New Dictionary 110 | Dim element As Variant 111 | For Each element In otherSetValues.Keys 112 | If thisSetValues.Exists(element) Then 113 | intersection_.Add key:=element, Item:=1 114 | End If 115 | Next element 116 | Set Intersection = intersection_ 117 | End Function 118 | 119 | Public Function Difference(otherSet As clsSet) As Dictionary 120 | Dim difference_ As Dictionary: Set difference_ = New Dictionary 121 | Dim thisSetDict As Dictionary: Set thisSetDict = CopyDict(Me.rowsAsSet) 122 | Dim otherSetDict As Dictionary: Set otherSetDict = CopyDict(otherSet.rowsAsSet) 123 | Dim element As Variant 124 | For Each element In otherSetDict.Keys 125 | If thisSetDict.Exists(element) Then 126 | thisSetDict.Remove element 127 | End If 128 | Next element 129 | Set Difference = thisSetDict 130 | End Function 131 | -------------------------------------------------------------------------------- /clsTestSet.cls: -------------------------------------------------------------------------------- 1 | VERSION 1.0 CLASS 2 | BEGIN 3 | MultiUse = -1 'True 4 | END 5 | Attribute VB_Name = "clsTestSet" 6 | Attribute VB_GlobalNameSpace = False 7 | Attribute VB_Creatable = False 8 | Attribute VB_PredeclaredId = False 9 | Attribute VB_Exposed = False 10 | Option Explicit 11 | 12 | Private m_rng1 As Range 13 | Private m_rng2 As Range 14 | Private m_mySet1 As clsSet 15 | Private m_mySet2 As clsSet 16 | Private Const ws1Name As String = "Set1" 17 | Private Const ws2Name As String = "Set2" 18 | 19 | Private Sub Class_Initialize() 20 | Call DeleteSheets 21 | Call AddSheets 22 | Call AddValuesToSheets 23 | Set m_rng1 = ActiveWorkbook.Worksheets(ws1Name).Range("A1:B10") 24 | Set m_rng2 = ActiveWorkbook.Worksheets(ws2Name).Range("A1:B10") 25 | Set m_mySet1 = New clsSet 26 | Set m_mySet2 = New clsSet 27 | m_mySet1.InputRng = m_rng1 28 | m_mySet2.InputRng = m_rng2 29 | End Sub 30 | Private Sub DeleteSheets() 31 | On Error Resume Next 32 | Dim ws1 As Worksheet: Set ws1 = ActiveWorkbook.Worksheets(ws1Name) 33 | Dim ws2 As Worksheet: Set ws2 = ActiveWorkbook.Worksheets(ws2Name) 34 | Application.DisplayAlerts = False 35 | ws1.Delete 36 | ws2.Delete 37 | Application.DisplayAlerts = True 38 | End Sub 39 | Private Sub AddSheets() 40 | Dim ws1 As Worksheet 41 | Dim ws2 As Worksheet 42 | Set ws1 = ActiveWorkbook.Worksheets().Add() 43 | Set ws2 = ActiveWorkbook.Worksheets().Add() 44 | ws1.Name = ws1Name 45 | ws2.Name = ws2Name 46 | End Sub 47 | Private Sub AddValuesToSheets() 48 | Dim arrMonths(12) As String 49 | Dim ws1 As Worksheet: Set ws1 = ActiveWorkbook.Worksheets(ws1Name) 50 | Dim ws2 As Worksheet: Set ws2 = ActiveWorkbook.Worksheets(ws2Name) 51 | Dim startRng1 As Range: Set startRng1 = ws1.Range("A1") 52 | Dim startRng2 As Range: Set startRng2 = ws2.Range("A1") 53 | Dim i As Integer 54 | arrMonths(0) = "Jan" 55 | arrMonths(1) = "Feb" 56 | arrMonths(2) = "Mar" 57 | arrMonths(3) = "Apr" 58 | arrMonths(4) = "May" 59 | arrMonths(5) = "Jun" 60 | arrMonths(6) = "Jul" 61 | arrMonths(7) = "Aug" 62 | arrMonths(8) = "Sep" 63 | arrMonths(9) = "Oct" 64 | arrMonths(10) = "Nov" 65 | arrMonths(11) = "Dec" 66 | For i = 0 To 9 67 | startRng1.Offset(i, 0).Value = arrMonths(i) 68 | startRng1.Offset(i, 1).Value = i + 1 69 | Next i 70 | For i = 2 To 11 71 | startRng2.Offset(i - 2, 0).Value = arrMonths(i) 72 | startRng2.Offset(i - 2, 1).Value = i + 1 73 | Next i 74 | End Sub 75 | 76 | Public Sub TestArrayValues() 77 | Dim rngValuesArray() As String: rngValuesArray = m_mySet1.rngValuesArray() 78 | Debug.Assert UBound(rngValuesArray) = 9 79 | Debug.Assert rngValuesArray(0) = "Jan" & vbTab & "1" 80 | Debug.Print "Method 'rngValuesArray' passes..." 81 | End Sub 82 | 83 | Public Sub TestRowsAsSet() 84 | Dim rowsAsSet As Dictionary: Set rowsAsSet = m_mySet1.rowsAsSet 85 | Debug.Assert rowsAsSet.Count = 10 86 | Debug.Print "Method 'rowsAsSet' passes...." 87 | End Sub 88 | 89 | Public Sub TestIsSuperset() 90 | Dim isSuperset_ As Boolean: 91 | isSuperset_ = m_mySet1.IsSuperset(m_mySet2) 92 | Debug.Assert isSuperset_ = False 93 | Debug.Print "Method 'IsSuperset' passes ...." 94 | End Sub 95 | 96 | Public Sub TestUnion() 97 | Dim union_ As Dictionary 98 | Set union_ = m_mySet1.UNION(m_mySet2) 99 | Debug.Assert union_.Count = 12 100 | Debug.Print "Method 'Union' passes ...." 101 | End Sub 102 | 103 | Public Sub TestIntersection() 104 | Dim intersection_ As Dictionary 105 | Set intersection_ = m_mySet1.Intersection(m_mySet2) 106 | Debug.Assert intersection_.Count = 8 107 | Debug.Assert Not intersection_.Exists("Jan" & vbTab & "1") 108 | Debug.Assert intersection_.Exists("Mar" & vbTab & "3") 109 | Debug.Print "Method 'Intersection' passes ...." 110 | End Sub 111 | 112 | Public Sub TestDifference() 113 | Dim difference_ As Dictionary 114 | Set difference_ = m_mySet1.Difference(m_mySet2) 115 | Debug.Assert difference_.Count = 2 116 | Debug.Assert difference_.Exists("Jan" & vbTab & "1") = True 117 | Debug.Assert difference_.Exists("Feb" & vbTab & "2") = True 118 | Debug.Print "Method 'Difference' passed ...." 119 | End Sub 120 | 121 | -------------------------------------------------------------------------------- /clsTranslateDna.cls: -------------------------------------------------------------------------------- 1 | VERSION 1.0 CLASS 2 | BEGIN 3 | MultiUse = -1 'True 4 | END 5 | Attribute VB_Name = "clsTranslateDna" 6 | Attribute VB_GlobalNameSpace = False 7 | Attribute VB_Creatable = True 8 | Attribute VB_PredeclaredId = False 9 | Attribute VB_Exposed = False 10 | Option Explicit 11 | ' SUMMARY: 12 | 'A class for translating DNA sequences into protein. 13 | 'It can translate amino acid mixtures that are specified in the DNA sequence as IUPAC ambiguity codes. 14 | 'This makes it suitable for translating highly polymorphic DNA, e.g. viral sequences such as those from Hepatitis C or HIV as well as mammal MHC sequences. 15 | 'Has just one public method "AminoAcidsForDNA" that returns a string array with elements containing one or more (if IUPAC ambiguity coding is used) amino 16 | 'acid single letter codes. 17 | 'Has one public property "version" that returns the code version. 18 | 'ASSUMES: 19 | 'The given DNA sequence is in-frame. 20 | 'CODE NOTES: 21 | 'Uses the class "clsKeyValueMap" as a simple dictionary. This class was used because development was done on the Mac 22 | 'where Excel does not have the dictionary object. Code for that class is given in this repository and described there. 23 | 'Translation code that ignores IUPAC ambiguity codes 24 | 'EXAMPLE USAGE: 25 | 'See test code in "modTest_clsTranslateDna.bas" 26 | 'TO DO: 27 | 'On Windows, the hand-rolled "clsKeyValueMap" could/should be replaced by the Microsoft Dictionary object. 28 | 'TO USE: 29 | 'Import this class and "clsKeyValueMap" into the VBA project and run the test code to ensure everything is ok. 30 | 'Nothing in this class is specific to any application that hosts VBA. It was developed in Excel but 31 | 'it SHOULD run in MS Word, MS Access, etc. But it has only been tested in Excel. 32 | 'AUTHOR: 33 | 'mick@javascript-spreadsheet-programming.com 34 | 35 | Private dictCodonToAA As clsKeyValueMap 'Maps three letter codons to single letter amino acid codes. 36 | Private dictIupacAmbig As clsKeyValueMap 'Maps nucleotide ambiguity codes to the nucleotides the represent. 37 | Private Const UNKNOWN_AMINOACID As String = "X" 'Character to be returned when a codon cannot be mapped to an amino acid. 38 | Private Const CODEVERSION As String = "Test" 39 | 'Class constructor. 40 | 'Sets two instance variables of class "clsKeyValueMap" and calls two methods to populate these instances 41 | 'One instance maps codons to single-letter amino acid codes (the translation table). 42 | 'The second maps IUPAC ambiguity codes to the nucleotide mixtures that they specify. 43 | Private Sub class_initialize() 44 | Set dictCodonToAA = New clsKeyValueMap 45 | Call PopulateDictCodonToAA 46 | Set dictIupacAmbig = New clsKeyValueMap 47 | Call PopulateDictIupacAmbig 48 | End Sub 49 | 'A flag to providing the code version of the class 50 | Public Property Get Version() As String 51 | Version = CODEVERSION 52 | End Property 53 | 'Populate the translation table using values specified in an array. 54 | 'Called by constructor 55 | 'Each element of the local array contains a codon-single letter amino acid code separated by a comma. 56 | 'The code processes each element by using the comma to split them into a two element array. 57 | 'The first element is the codon, the second the single letter amino acid code. 58 | 'The paired values are used to populate the "clsKeyValueMap" instance. 59 | Private Sub PopulateDictCodonToAA() 60 | Dim i As Long 61 | Dim arrPair() As String 62 | Dim codonsToAminoAcids As Variant 63 | 64 | codonsToAminoAcids = Array("TAA,*", "TAG,*", "TGA,*", _ 65 | "GCA,A", "GCC,A", "GCG,A", "GCT,A", _ 66 | "TGC,C", "TGT,C", _ 67 | "GAC,D", "GAT,D", _ 68 | "GAA,E", "GAG,E", _ 69 | "TTC,F", "TTT,F", _ 70 | "GGA,G", "GGC,G", "GGG,G", "GGT,G", _ 71 | "CAC,H", "CAT,H", _ 72 | "ATA,I", "ATC,I", "ATT,I", _ 73 | "AAA,K", "AAG,K", _ 74 | "CTA,L", "CTC,L", "CTG,L", "CTT,L", "TTA,L", "TTG,L", _ 75 | "ATG,M", _ 76 | "AAC,N", "AAT,N", _ 77 | "CCA,P", "CCC,P", "CCG,P", "CCT,P", _ 78 | "CAA,Q", "CAG,Q", _ 79 | "AGA,R", "AGG,R", "CGA,R", "CGC,R", "CGG,R", "CGT,R", _ 80 | "AGC,S", "AGT,S", "TCA,S", "TCC,S", "TCG,S", "TCT,S", _ 81 | "ACA,T", "ACC,T", "ACG,T", "ACT,T", _ 82 | "GTA,V", "GTC,V", "GTG,V", "GTT,V", _ 83 | "TGG,W", _ 84 | "TAC,Y", "TAT,Y") 85 | 86 | For i = 0 To UBound(codonsToAminoAcids) 87 | arrPair = Split(codonsToAminoAcids(i), ",") 88 | Call dictCodonToAA.addKeyValuePair(arrPair(0), arrPair(1)) 89 | Next i 90 | 91 | End Sub 92 | 'Populate the lookup table mapping IUPAC ambiguity codes to a string of the nucleotide mixtures they represent. 93 | 'Called by constructor. 94 | 'A local array stores the mappings as strings in the format "nucleotide-comma-ambiguity code" 95 | 'Each element is processed by splitting on the comma to produce a two-element string array. 96 | 'The first element is the key, the second the value of the lookup table (instance of of class "clsKeyValueMap") 97 | 'populated in the "for" loop. 98 | 'NOTE:Only IUPAC ambiguity codes specifying two nucleotides are included. The others could be added but 99 | 'have been omitted here to ensure that the amino acid counts at ambiguous positions does not become over-large. 100 | Private Sub PopulateDictIupacAmbig() 101 | Dim arrAmbigsNucs As Variant 102 | Dim i As Long 103 | Dim arrPair() As String 104 | 105 | arrAmbigsNucs = Array("R,AG", "Y,CT", "K,GT", "M,AC", "S,CG", "W,AT", "A,A", "C,C", "G,G", "T,T") 106 | 107 | For i = 0 To UBound(arrAmbigsNucs) 108 | arrPair = Split(arrAmbigsNucs(i), ",") 109 | Call dictIupacAmbig.addKeyValuePair(arrPair(0), arrPair(1)) 110 | Next i 111 | 112 | End Sub 113 | 'Only public method of class. 114 | 'Performs the translation of given DNA sequence by calling one of two other private methods. 115 | 'If its second argument is "True" it calls method "TranslateDnaAmbig" to do a translation that handles IUPAC ambiguity codes. 116 | 'Otherwise, it does a simple translation by calling "TranslateDNANonAmbig" that returns the value of the constant 117 | '"UNKNOWN_AMINOACID" for any codon containing any characters other than A,C,G,T. 118 | 'Return a string array where each element is an amino acid, the value of "UNKNOWN_AMINOACID", 119 | 'or a mixture of amino acids (if second argument = "True") 120 | 'All input sequence is converted to upper case before processing. 121 | Public Function AminoAcidsForDNA(dnaSeq As String, iupacAmbig As Boolean) As String() 122 | dnaSeq = UCase(dnaSeq) 123 | If iupacAmbig Then 124 | AminoAcidsForDNA = TranslateDnaAmbig(dnaSeq) 125 | Else 126 | AminoAcidsForDNA = TranslateDNANonAmbig(dnaSeq) 127 | End If 128 | End Function 129 | 130 | 'Simple translation of DNA sequence. 131 | 'Called by method "AminoAcidsForDNA" when this method's second argument is "False". 132 | 'Process input sequence by retrieving consecutive codons and return a string array of the amino acid codes from the translation table. 133 | 'The translation table returns a VB null string if the codon is not found, this is converted to the value in constant "UNKNOWN_AMINOACID". 134 | Private Function TranslateDNANonAmbig(dnaSeq As String) As String() 135 | Dim arrAminoAcids() As String 136 | Dim seqLen As Long: seqLen = Len(dnaSeq) 137 | Dim i As Long: i = 1 138 | Dim elementCounter As Long: elementCounter = 0 139 | Dim codon As String 140 | Dim aminoAcid As String 141 | 142 | For i = 1 To seqLen Step 3 143 | codon = Mid(dnaSeq, i, 3) 144 | aminoAcid = dictCodonToAA.ValueForKey(codon) 145 | If aminoAcid = vbNullString Then 146 | aminoAcid = UNKNOWN_AMINOACID 147 | End If 148 | ReDim Preserve arrAminoAcids(elementCounter) 149 | arrAminoAcids(elementCounter) = aminoAcid 150 | elementCounter = elementCounter + 1 151 | Next i 152 | 153 | TranslateDNANonAmbig = arrAminoAcids 154 | 155 | End Function 156 | 'Translates DNA so that IUPAC nucleotide ambiguity codes are recognised. 157 | 'Called by method "AminoAcidsForDNA" when this method's second argument is "True". 158 | 'Process each codon in a loop: 159 | ' If the codon is in translation table, retrieve the associated amino acid code. 160 | ' Else pass the codon to method "GetMixedCodons" with the mixed codon as its argument. 161 | ' Pass the returned array of codons to another method, "GetMixedAminoAcids" and join its returned array with an empty string. 162 | ' Push the amino acid code(s) onto a string array and return this array when the loop has completed. 163 | Private Function TranslateDnaAmbig(dnaSeq As String) As String() 164 | Dim arrAminoAcids() As String 165 | Dim seqLen As Long: seqLen = Len(dnaSeq) 166 | Dim i As Long: i = 1 167 | Dim j As Long: j = 0 168 | Dim elementCounter As Long: elementCounter = 0 169 | Dim codon As String 170 | Dim arrMixedCodons() As String 171 | Dim arrMixedAminoAcids() As String 172 | Dim aminoAcid As String 173 | 174 | For i = 1 To seqLen Step 3 175 | codon = Mid(dnaSeq, i, 3) 176 | aminoAcid = dictCodonToAA.ValueForKey(codon) 177 | If aminoAcid = vbNullString Then 178 | arrMixedCodons = GetMixedCodons(codon) 179 | arrMixedAminoAcids = GetMixedAminoAcids(arrMixedCodons) 180 | aminoAcid = Join(arrMixedAminoAcids, "") 181 | End If 182 | ReDim Preserve arrAminoAcids(elementCounter) 183 | arrAminoAcids(elementCounter) = aminoAcid 184 | elementCounter = elementCounter + 1 185 | Next i 186 | 187 | TranslateDnaAmbig = arrAminoAcids 188 | 189 | End Function 190 | 'For any codon with nucleotide IUPAC ambiguity codes, return a string array containing all the potential codons present. 191 | 'If the given codon contains any character not specifed in the lookup table keys (instance variable "dictIupacAmbig") 192 | 'return a single element array containing the constant "UNKNOWN_AMINOACID" repeated three times (the "If .. Or.."). 193 | 'This is a tricker method than the others so there is in-line comment. 194 | Private Function GetMixedCodons(codon As String) As String() 195 | Dim arrMixedCodons() As String 196 | Dim codonCount As Long: codonCount = 0 197 | 'Extract each letter from the given codon. 198 | Dim mix1 As String: mix1 = Mid(codon, 1, 1) 199 | Dim mix2 As String: mix2 = Mid(codon, 2, 1) 200 | Dim mix3 As String: mix3 = Mid(codon, 3, 1) 201 | Dim i As Long: i = 1 202 | Dim j As Long: j = 1 203 | Dim k As Long: k = 1 204 | Dim nuc1 As String 205 | Dim nuc2 As String 206 | Dim nuc3 As String 207 | 208 | 'Check if there are any unrecognised letters, if there are, then exit the function 209 | 'returning a "dummy" codon that wiil resolve to "UNKNOWN_AMINOACID" in the translation. 210 | If dictIupacAmbig.ValueForKey(mix1) = vbNullString Or _ 211 | dictIupacAmbig.ValueForKey(mix2) = vbNullString Or _ 212 | dictIupacAmbig.ValueForKey(mix3) = vbNullString Then 213 | ReDim Preserve arrMixedCodons(0) 214 | arrMixedCodons(0) = UNKNOWN_AMINOACID & UNKNOWN_AMINOACID & UNKNOWN_AMINOACID 215 | GetMixedCodons = arrMixedCodons 216 | Exit Function 217 | End If 218 | 219 | 'Worst bit of code! 220 | 'Process characters 1, 2, and 3 from the given codon by looping one character at a time over the string values they return from 221 | '"dictIupacAmbig". 222 | 'Assemble codons that the mixed input codons represent in the inner-most "for" loop. 223 | 'Push each codon onto an array and ,once the loops have completed, return the array. 224 | For i = 1 To Len(dictIupacAmbig.ValueForKey(mix1)) 225 | nuc1 = Mid(dictIupacAmbig.ValueForKey(mix1), i, 1) 226 | For j = 1 To Len(dictIupacAmbig.ValueForKey(mix2)) 227 | nuc2 = Mid(dictIupacAmbig.ValueForKey(mix2), j, 1) 228 | For k = 1 To Len(dictIupacAmbig.ValueForKey(mix3)) 229 | nuc3 = Mid(dictIupacAmbig.ValueForKey(mix3), k, 1) 230 | codon = nuc1 & nuc2 & nuc3 231 | ReDim Preserve arrMixedCodons(codonCount) 232 | arrMixedCodons(codonCount) = codon 233 | codonCount = codonCount + 1 234 | Next k 235 | Next j 236 | Next i 237 | 238 | GetMixedCodons = arrMixedCodons 239 | 240 | End Function 241 | 'Given an array of codons wheere all IUPAC ambiguities have been resolved, 242 | 'return a string array containing the codes for the amino acids that they encode. 243 | 'The amino acid translations are added as keys to an instance of "clsKeyValueMap". 244 | 'This is done to prevent duplicates appearing in the returned array. 245 | 'Duplicates arise due to the redundancy of the genetic code where multiple codons specify the same amino acid. 246 | 'Example "CAA" and "CAG" both specify "Q" so a "CAR" would resolve to "CAA" and "CAG" giving two "Q"'s 247 | 'in the output. Making them lookup keys that map to a dummy value ("1" here") suppresses these duplicates. 248 | Private Function GetMixedAminoAcids(arrMixedCodons() As String) As String() 249 | Dim i As Long: i = 0 250 | Dim dictAminoAcid As clsKeyValueMap 251 | Dim aminoAcid As String 252 | Dim arrMixedAminoAcids() As String 253 | 254 | 255 | Set dictAminoAcid = New clsKeyValueMap 256 | 257 | For i = 0 To UBound(arrMixedCodons) 258 | aminoAcid = dictCodonToAA.ValueForKey(arrMixedCodons(i)) 259 | If aminoAcid = vbNullString Then 260 | aminoAcid = UNKNOWN_AMINOACID 261 | End If 262 | Call dictAminoAcid.addKeyValuePair(aminoAcid, "1") 263 | Next i 264 | 265 | GetMixedAminoAcids = dictAminoAcid.Keys 266 | 267 | Set dictAminoAcid = Nothing 268 | 269 | End Function 270 | 271 | -------------------------------------------------------------------------------- /misc_functions.bas: -------------------------------------------------------------------------------- 1 | Option Explicit 2 | 3 | ' Run the CLEAN function over a sheet used range and overwrite the original with 4 | ' the CLEANed version. 5 | Sub CleanCells() 6 | Dim sh As Worksheet 7 | Dim usedRng As Range 8 | Dim cell As Range 9 | Set sh = ActiveWorkbook.Worksheets("Sheet1") 10 | Set usedRng = sh.UsedRange 11 | MsgBox usedRng.Address 12 | For Each cell In usedRng.Cells 13 | cell.Value = Application.WorksheetFunction.Clean(cell.Value) 14 | Next cell 15 | End Sub 16 | -------------------------------------------------------------------------------- /modCopySheetsToNewWorkbook.bas: -------------------------------------------------------------------------------- 1 | Attribute VB_Name = "modMain" 2 | Option Explicit 3 | 4 | 5 | Sub CopySheetUsedRng(pathToInputXLFile As String, _ 6 | fromSheetName As String, _ 7 | pathToOutputXLFile As String, _ 8 | toSheetName As String) 9 | Dim inputWbk As Workbook 10 | Dim outputWbk As Workbook 11 | Dim fromSheet As Worksheet 12 | Dim toSheet As Worksheet 13 | Dim toCellRng As Range 14 | Dim toUsedRngRowCount As Long 15 | Set inputWbk = Application.Workbooks().Open(pathToInputXLFile) 16 | Set outputWbk = Application.Workbooks().Open(pathToOutputXLFile) 17 | Set fromSheet = inputWbk.Worksheets(fromSheetName) 18 | Set toSheet = outputWbk.Worksheets(toSheetName) 19 | toUsedRngRowCount = toSheet.UsedRange.Rows.Count 20 | If toSheet.Range("A1").Value = "" Then 21 | Set toCellRng = toSheet.Range("A1") 22 | Else 23 | Set toCellRng = toSheet.Range("A1").Offset(toUsedRngRowCount, 0) 24 | End If 25 | fromSheet.UsedRange.Copy toCellRng 26 | inputWbk.Close False 27 | outputWbk.Close True 28 | End Sub 29 | 30 | 31 | Sub RunCopySheetUsedRng() 32 | Dim pathToInputXLFile As String 33 | Dim pathToOutputXLFile As String: pathToOutputXLFile = "\concatenated_file.xlsx" 34 | Dim fromSheetName As String: fromSheetName = "Sheet1" 35 | Dim toSheetName As String: toSheetName = "Sheet1" 36 | Dim i As Integer 37 | Application.ScreenUpdating = False 38 | For i = 1 To 609 39 | pathToInputXLFile = "\table_" & i & ".xlsx" 40 | Call CopySheetUsedRng(pathToInputXLFile, fromSheetName, pathToOutputXLFile, toSheetName) 41 | Next i 42 | Application.ScreenUpdating = True 43 | MsgBox "Complete!" 44 | End Sub 45 | -------------------------------------------------------------------------------- /modTest_clsSets.bas: -------------------------------------------------------------------------------- 1 | Attribute VB_Name = "modTest_clsSets" 2 | Option Explicit 3 | Sub testClass() 4 | Dim testCls As clsTestSet: Set testCls = New clsTestSet 5 | Call testCls.TestArrayValues 6 | Call testCls.TestRowsAsSet 7 | Call testCls.TestIntersection 8 | Call testCls.TestIsSuperset 9 | Call testCls.TestUnion 10 | ' Re-calling to test for mutation of set1, expect False 11 | Call testCls.TestIsSuperset 12 | Call testCls.TestDifference 13 | End Sub 14 | 15 | -------------------------------------------------------------------------------- /modTest_clsTranslateDna.bas: -------------------------------------------------------------------------------- 1 | Attribute VB_Name = "modTest_clsTranslateDna" 2 | Option Explicit 3 | 'To test code in class "clsTranslateDna". 4 | Sub Run_Test_clsTranslateDna() 5 | Dim dnaSeq As String 6 | 'See link for source sequence: 7 | 'http://www.hiv.lanl.gov/components/sequence/HIV/asearch/query_one.comp?se_id=AB025113 8 | 'Using HIV-1 protease sequence as test. 9 | dnaSeq = "cctcagatcactctttggcaacgacccctcgtcacaataaggataggggggcagctaaaggaagctctattagatacaggagcagatgatacagtattagaagaaatgaatttgccaggaagatggaaaccaaaaatgatagggggaattggaggttttatcaaagtaagacagtatgatcagatacccatagaaatcagtggaaagaaagctataggtacagtattaataggacctacacctgtcaacataattggaagaaatctgttgactcagattggctgcactttaaatttt" 10 | Call Test_clsTranslateDna(dnaSeq) 11 | End Sub 12 | 'Output is compared to translation given on page referenced above. 13 | 'Debug.Assert will throw an error if the translation performed here does not equal the expected result. 14 | Sub Test_clsTranslateDna(dnaSeq As String) 15 | If Len(dnaSeq) < 3 Then 16 | Debug.Print "Input sequence " & "'" & "'" & " < one codon in length. Exiting......" 17 | Exit Sub 18 | End If 19 | Dim translator As clsTranslateDna 20 | Set translator = New clsTranslateDna 21 | Dim arrAA() As String 22 | Dim i As Long 23 | Dim givenTranslation As String 24 | 'Translation given on link above, take this as "correct" 25 | givenTranslation = "PQITLWQRPLVTIRIGGQLKEALLDTGADDTVLEEMNLPGRWKPKMIGGIGGFIKVRQYDQIPIEISGKKAIGTVLIGPTPVNIIGRNLLTQIGCTLNF" 26 | 27 | 'Translate ignoring IUPAC ambiguity codes. 28 | arrAA = translator.AminoAcidsForDNA(dnaSeq, False) 29 | Debug.Assert (Join(arrAA, "") = givenTranslation) 30 | Debug.Print "Output WITHOUT IUPAC ambiguity Code Translation" 31 | For i = 0 To UBound(arrAA) 32 | Debug.Print i + 1, ": ", arrAA(i) 33 | Next i 34 | 35 | 'Translate taking IUPAC ambiguity codes into account. 36 | Debug.Print "Output WITH IUPAC ambiguity Code Translation" 37 | arrAA = translator.AminoAcidsForDNA(dnaSeq, True) 38 | Debug.Assert (Join(arrAA, "") = givenTranslation) 39 | Debug.Print Join(arrAA, "") 40 | For i = 0 To UBound(arrAA) 41 | Debug.Print i + 1, ": ", arrAA(i) 42 | Next i 43 | 44 | 'Precautionary cleanup 45 | Set translator = Nothing 46 | 47 | End Sub 48 | 49 | -------------------------------------------------------------------------------- /name_ranges.bas: -------------------------------------------------------------------------------- 1 | ' Name all the cells of a sorted column using the unique values 2 | ' of the starting cell column. 3 | ' Used to name blocks of ranges. 4 | Sub AssignNamesToRange(rngForNames As Range, startCell As Range) 5 | Dim rngRowCount As Long: rngRowCount = rngForNames.Rows.Count 6 | Dim rngColCount As Long: rngColCount = rngForNames.Columns.Count 7 | Dim startCellColNum As Long: startCellColNum = startCell.Column 8 | Dim rngFirstColNum As Long: rngFirstColNum = rngForNames.Cells(1, 1).Column 9 | Dim rngLastColNum As Long: rngLastColNum = rngFirstColNum + rngColCount 10 | Dim leftColOffset As Long: leftColOffset = rngFirstColNum - startCellColNum 11 | Dim rightColOffset As Long: rightColOffset = rngLastColNum - startCellColNum 12 | Dim rngTopLeftCell As Range 13 | Dim rngBottomRightCell As Range 14 | Dim nextRowNum As Long 15 | Dim rngStart As Long 16 | Dim i As Long 17 | Dim j As Long 18 | Dim rngName As String 19 | Do While i <= rngRowCount 20 | j = 0 21 | Do While startCell.Offset(j, 0).Value = startCell.Value 22 | j = j + 1 23 | Loop 24 | rngName = startCell.Value 25 | Set rngTopLeftCell = startCell.Offset(0, leftColOffset) 26 | Set rngBottomRightCell = startCell.Offset(j - 1, rightColOffset - 1) 27 | Range(rngTopLeftCell, rngBottomRightCell).Name = rngName 28 | Set startCell = startCell.Offset(j, 0) 29 | If startCell.Row > rngRowCount Then Exit Do 30 | i = i + j 31 | Loop 32 | End Sub 33 | 34 | Sub RunAssignNamesToRange() 35 | Dim rngForNames As Range: Set rngForNames = ActiveWorkbook.Worksheets("Sheet1").UsedRange 36 | Dim startCell As Range: Set startCell = ActiveWorkbook.Worksheets("Sheet1").Range("C1") 37 | Call AssignNamesToRange(rngForNames, startCell) 38 | End Sub 39 | -------------------------------------------------------------------------------- /transcript.md: -------------------------------------------------------------------------------- 1 | Create custom functions in Excel 2 | Selecting transcript lines in this section will navigate to timestamp in the video 3 | - [Curt] Microsoft Excel is a powerful and versatile tool for analyzing data within your business or organization. The new LAMBDA and LET 4 | functions make it possible for you to create powerful and portable custom calculations you can use just like built-in Excel functions. In this 5 | course, I will show you the Excel formula language and provide real-world examples to demonstrate how you can apply the incredible power of 6 | custom functions to your Excel workbooks. I'm Curt Frye. Join me at LinkedIn Learning for an introduction to the essential skills that will let 7 | you unlock the power and flexibility that comes with creating your own custom functions in Microsoft Excel. 8 | 9 | What you should know before starting 10 | Selecting transcript lines in this section will navigate to timestamp in the video 11 | - [Instructor] Thanks for your interest in this course. Before I get started, I would like to give you some information about what you should 12 | know to get the most out of this course. First, you should definitely know how to create formulas in Excel. You don't need to be the most 13 | advanced user, but the more you know, the more you will get out of this course, at least at the start. You should also know a lot about your 14 | business and how to determine what information you need to solve a problem. Creating custom functions lets you streamline your work, so it's 15 | important to know what it is you need to do. Also, know that Lambda works best with other functions. You do not need to create everything by 16 | hand. Use the built-in functions in Excel, and you'll save yourself a lot of time. Also, be aware that Excel from Microsoft 365 updates 17 | regularly. That means that your screen might not look exactly like mine, but everything will still be there. And what's even more exciting is 18 | that new functions are added all the time, so you'll have even more capability than what I have today. Also, be aware that this is a course for 19 | beginning and intermediate users, so there will be some repetition to emphasize important concepts. Also, I will create named functions for 20 | some, but not all, examples. It all depends on how long I feel a particular movie is running. And I also have a chapter on scenarios that 21 | encourage you how to think about your own solutions. I have built some custom calculations for problems that I have spoken about in the past, 22 | and hopefully that will provide you with incentives and insight into the type of functions that you can create for yourself. 23 | 24 | Explore custom functions in Excel VBA 25 | Selecting transcript lines in this section will navigate to timestamp in the video 26 | - [Instructor] The ability to create custom functions in Excel within a worksheet is an important step forward. However, I think it's important 27 | to know where we have come from. So in this movie, I will show you the old method of creating custom functions using Visual Basic for 28 | Applications, or VBA. My sample file is 01_01_VBAFunction, and that is a macro-enabled Excel workbook you can find in the chapter one folder of 29 | the exercise files collection. So the goal for this worksheet is to calculate a commission. I have a set of sales starting in cell B4 and then I 30 | want to calculate commissions. I can do that using a function that I've created using Visual Basic for Applications. To move to the Visual Basic 31 | editor, I will press alt-F11 and then I will go over to the left side in the project window and double-click Module 1. And there you can see the 32 | function that I created. I won't go through it in depth but you can see at the top, I have a function and then its name is Commission. That's 33 | what we'll use as the function when we create the formula. It takes a currency value as an input and then it looks to see if sales are less than 34 | 500. If so, 5% commission, greater than 500 but less than 1,000, then 6%, anything 1,000 or greater, 7%, and, at the bottom, it returns the 35 | value that was calculated. I'll press alt-F11 to move back and now I can create my formula. So I will select cells C4 through C6, equal, and 36 | then I'll type in commission which is the new function that I defined, and then I'll type b4. That'll be for the first formula, and then 37 | Control+Enter, and you see, I get the commissions on each of those sales. One of the limitations of working with VBA might have happened to you 38 | when you were trying to open this file. Your macro security settings might have either prevented it silently or they might have flashed a 39 | warning, or your company's IT policy might not have allowed the file to open on your system. And those cases indicate the possibility that a 40 | macro could be written that would be harmful to your computer. So rather than do that, the Excel product team at Microsoft has allowed us to 41 | create our own functions using the Excel formula language and that is what we'll focus on from here on out. 42 | 43 | Describe the Excel formula language 44 | Selecting transcript lines in this section will navigate to timestamp in the video 45 | - [Narrator] You can create custom functions in Excel now, using the Excel formula language. Existing functions accept inputs and generate 46 | results. And here you have three different ways to create a SUM formula. You use the SUM function, looking at the range, A1 through A15 or the 47 | individual cells A1 through A15 or two separate ranges, A1 to A5 and A11 to A15. So ignoring cells A6 to A10. The problem at least if you want 48 | to create your own functions is that the calculation is not visible to the user. One benefit of using LAMBDA is that you can define the inputs 49 | and the calculation and make them explicit. That is make them visible within your workbook. Here is what a LAMBDA function might look like. 50 | Amount and rate are inputs and amount times one plus rate is the calculation. And at the end you see that I have A2 and B2 in their own set of 51 | parentheses. Those are the cells that provide values for the amount and the rate that are used in the calculation. You can also use the Name 52 | Manager to create a new function. For example, you might name your function ApplyGrowth and accept the amount and rate as part of the formula 53 | that you create. With that as background, I'll switch to Excel and show you what it looks like within a workbook. I have switched over to Excel 54 | and the name of the workbook I'm using is 01_02_Describe and you can find it in the chapter one folder of the exercise files collection. In this 55 | workbook, I have two starting amounts and growth rates. And in cell C2 you can see that I have a LAMBDA function although I have it as text 56 | because I didn't put an equal sign in front. That basically creates the function that I showed you earlier in this moving. So if I want to see 57 | how that would work I will edit the formula in cell C2 and add an equal sign in front. So now it's a formula, enter and we get the value of 58 | 104,750. And if we take a look back, we'll see that once again this function accepts its inputs from A2 and B2. If I were to take those away by 59 | editing them out and press enter, I would get a calc error. And that's because the formula as is defined right now doesn't have any inputs. So 60 | I'll press Control Z to bring them back. I've also created a function that will allow me to use this LAMBDA as a named formula. So if I go to 61 | the formulas tab and click Name Manager you can see in the Name Manager that I have a name called ApplyGrowth. And that is actually a custom 62 | function. And if you think it's weird that you have to go to the Name Manager, which is usually used to name ranges, to create a custom function 63 | then the Excel product team agrees with you. It's just this is the best solution they could find for right now. So it'll probably change in the 64 | future. But for now, this is where you can find it. And if you look at the bottom, you'll see in Refers to: that we have the formula that I 65 | defined before LAMBDA and then it accepts amount and rate and then it multiplies the amount by one plus the rate. And also because we are 66 | accepting amount and rate as inputs we don't have the A2 and B2 at the end. Okay, with that in mind, I'll click close. And then in cell C3 I'll 67 | type equal and ApplyGrowth as our function. And the amount is in cell A3, The rate is in B3, rare parenthesis to close and enter. And we get our 68 | result of $212,000. 69 | 70 | Describe the goals of formula programming 71 | Selecting transcript lines in this section will navigate to timestamp in the video 72 | - [Instructor] When I mention Lambda to Excel users who haven't heard of it, they are interested in knowing what the goals are for formula 73 | programming in Excel or functional programming. And there are quite a few of them, but the four I want to focus on start with the ability to 74 | create custom functions used throughout a workbook. You can define a function, such as applied growth rate. And because you created it, you know 75 | exactly what it's going to do. It also allows the function to be copied to other workbooks. You can go to the name manager, copy the Lambda 76 | definition from the named range that refers to your custom function, and copy it into another workbook. Now, of course, if you want your 77 | formulas to work in that other workbook, you need to make sure that you give it exactly the same name in the name manager, but that's something 78 | that you'll figure out very quickly in case you make a mistake. The overarching goal is to create an analytical platform that does not rely on 79 | code. Visual Basic for Applications, the macro programing language, is incredibly powerful. But because it can do almost anything within Excel, 80 | it means that it can be used to violate your computer security. Macro viruses were a real problem for a long time, so that means the companies 81 | clamp down on your ability to use macros and VBA. That's understandable, and the formula programming language through Lambda takes away that 82 | vulnerability. But finally, going into the future, using Lambda will enable advanced applications, such as machine learning. The map and reduced 83 | functions are extremely powerful and are frequently used in machine learning applications. So even though I don't go into any of that in this 84 | course, if you want to use Excel as an advanced analytical platform, then you'll be able to do so going forward. Perhaps not right at the 85 | moment, at least not as much as you would like to, but going forward in the future, I think you'll see those capabilities come online very 86 | quickly. Manage formulas on the Formula Bar 87 | Selecting transcript lines in this section will navigate to timestamp in the video 88 | - [Instructor] When you create formulas, including those that use lambda and let functions you do so on the formula bar. In this movie, I will 89 | show you how to work with the formula bar in ways you might not have in your everyday Excel use. My sample file is 0104 formula bar and you can 90 | find it in the chapter one folder of the exercise files collection. I have created an IFS, that is a multiple condition IF formula and I am 91 | rounding the value up to zero digits so it will be a whole number. And you can see the formula, which is in cell B4 currently displayed on the 92 | formula bar and it's a long one. So let's say that I want to make that formula easier to read by expanding the formula bar. I can do that a 93 | little bit by going to the right edge of the formula bar and clicking its down arrow here. And that expands it to three lines of text. And one 94 | way that you can use that extra space is to add line breaks within the formula. So for example, I will click to the right of the first left 95 | parentheses and then press alt enter. Doing so adds a line break and I could do the same thing to the left of A4 and to the right of the next 96 | left parenthesis. So alt enter and I have run out of space but what I can do is hover the mouse pointer over the bottom edge and when it turns 97 | to an up and down pointing arrow, I can drag down and you see that it's adding space as I go. Now it's taking that space away from the worksheet 98 | itself. So you see that I only have a couple of rows left at the bottom, especially because I'm zoomed in. So you need to balance your need for 99 | space in the formula bar versus what you need to see in the worksheet. And now I will add lines. So I'll click to the right of the next comma 100 | after A4 times 2.5. So I have my condition and then my result. So alt enter, and then I'll go to the right of the comma after the next 101 | condition, alt enter. And I'll do the same thing here and I will save you the commentary because the reasoning and the actions are all the same. 102 | So there we go. And then I will go to the end of ROUNDUP. And there we have it. So this is one way to lay it out. And because I have some space 103 | left over I can drag the formula bar back up so it only shows as much as I need. And now if I press enter, the formula's entered and the formula 104 | bar stays in the same configuration. If I want to close the formula bar, that is to reduce its space, then I can click the up arrow here and 105 | we're all the way back up. But of course the issue is that I only see the first line and so I see ROUNDUP and then left parentheses and nothing 106 | else. So to see the rest of it, I need to expand the formula bar and it goes back to the condition that it was in before. So if you're creating 107 | a long formula that is hard to read you can use the expanded formula bar and alt enter to make it easier to read. And if it seems like you're 108 | not seeing all the formula on the formula bar, then you can expand it. And I bet that you'll see more lines laid out like you see here. 109 | 110 | Define a function using LAMBDA 111 | Selecting transcript lines in this section will navigate to timestamp in the video 112 | - One great new capability in Excel is that you are now able to define a custom function in the program using lambda. In this movie, I will show 113 | you what that process looks like. My sample file is oh two oh one create lambda, and you can find it in the chapter two folder of the exercise 114 | files collection. In this workbook, I have a worksheet that displays values. So we have a starting amount, and then the growth rate. And then to 115 | the left, we have a number of years. If you look at the formula in cell B three, you'll see that the starting amount for the year 2024 is the 116 | ending amount in D two for 2023. So we're able to continue on with our growth. If I want to create a lambda function to calculate the interest 117 | that's been earned, then I can type an equal sign; I'll start in D two, and lambda is the name of the function. I need to provide it with two 118 | separate inputs, So I'll have start, underscore amount, then a comma, and then G underscore rate. So we have our starting amount and our growth 119 | rate, then a comma, and the calculation is start, amount and you can see it shows up in the auto complete list, and we'll multiply that by in 120 | parentheses one plus the growth rates that's G underscore R A T E. Then two right parentheses, and it's easy to think that we're done, but we'll 121 | see an error when I press enter. And that error is calc, and what that indicates is that there's a calculation engine error and the problem, if 122 | you look at the formula in the formula bar, is that we don't have any inputs. We just have definitions. So if I want to add those inputs, I can 123 | double click D two. And then in a new set of parentheses I need to type in the cell addresses for the inputs. So the starting amount is in B 124 | two, then a comma, and the growth rate is in C two. So I have those and parentheses, and enter, and there's my test value. I get the ending 125 | amount of 106,000 and then that's the starting amount for 2024. Now I'll go to sell D two and double click the fill handle at the bottom right 126 | corner and that copies the growth rate all the way down. So I have the ending amounts and I have the formulas that I need. So those are the 127 | basics of creating a function using lambda within Microsoft Excel. 128 | 129 | Assign a function name to a LAMBDA 130 | Selecting transcript lines in this section will navigate to timestamp in the video 131 | - After you create a Lambda function you can assign it a name using the name manager. In this movie, I will demonstrate how to do that. My 132 | sample file is 0 2 0 2 name Lambda, and you can find it in the chapter two folder of the exercise files collection. I have cell D2 selected at 133 | the moment. I'll go ahead and double click it so we can see the formula that is inside of it. This formula, which was created using the Lambda 134 | function, has a start amount and a growth rate as the two arguments it receives. And then it calculates a result that is the amount earned or 135 | the amount at the end of the year by multiplying the start amount by one, plus the growth rate. And then at the end, again, we have inputs from 136 | cell B2, which is the first input, the start amount, and then C2 is the growth rate. If I want to create a named function based on this lambda, 137 | then I can copy everything up to the inputs at the end. So I will copy everything there. So I have the equal sign and the second right 138 | parenthesis at the end. So I'll go ahead and press control C to copy then escape to exit the cell editing mode. Now I need to go to the name 139 | manager, So I'll go to the formulas tab and then click name manager. And then in the name manager, I'll click new and I can enter in a new name. 140 | And for this, I will call it total amount. And one thing to note is that I used total with a capital T and then amount with a capital A, But all 141 | the other letters are lowercase. The reason I did that is because Excel's built-in functions have all capital letters as their function names, 142 | and this is a way of reminding myself that this is a function that I created. And the usual rules for named ranges apply. You can't start it 143 | with a number, you can't have any spaces, and so on. The scope is the workbook, and for the comment, I will just say calculate total amount 144 | after interest. And then, I'll go down to the refers to box and replace the existing text with the formula that I copied. Everything looks good, 145 | so I'll go ahead and click okay. And there I have my Lambda function called total amount. So I'll click close, and then with cell D2 still 146 | selected, I'll type equal total amount. And you can see that I have a little bit of indicator text saying that it calculates the total amount 147 | after interest. So I'll press tab, and then I also have a tool tip that asks for the two arguments. So I have the start amount, which for this 148 | row is in B2 and then the growth rate is in C2, right parentheses, and enter. And I get the same value as before, but as you can see, the 149 | formula is much more readable. Then I can double-click the fill handle at the bottom right of cell D2, and I get the same results, but as 150 | before, I have total amount that accepts two arguments from the cells in this particular row. 151 | 152 | Define a variable using LET 153 | Selecting transcript lines in this section will navigate to timestamp in the video 154 | - A Lambda statement, lets you create, reusable custom functions in Excel. A Let statement, by contrast gives you the ability to define 155 | variables that can be used within those Lambda functions. In this movie, I will show you the first part of the process, which is how to define a 156 | variable using Let. My sample file is, 0 2 0 3 Let, and you can find it in the chapter two folder, of the Exercise Files collection. In this 157 | workbook I have a list of cases and also varieties. So let's say that we're in a warehouse, and I have cases, that contain varieties of seed, 158 | that I want to use on a farm, and they've been distributed randomly. And you see that in column B. Case one contains Variety four, so does 159 | column two. Case three contains Variety three, and you can see the rest. Let's say there are more than four varieties possible, and I want to 160 | have a list of all the unique varieties that actually were selected for this particular case. To do that, I can go to Cell D four and then type 161 | equal. And the function I'll use is fairly new, and that is unique. And this finds the unique values, within a data set. And my array is B four 162 | to B 12. I don't need to change anything else, 'cause I'm just looking for unique values, right Parentheses and enter. And the result of the 163 | formula starts in cell D four, and then spills to cells D five through D seven. So I have four unique varieties. This isn't all that easy to 164 | read though, because they are not listed in alphabetical order. I can change that by using the sort function, which is also relatively new. So I 165 | will double click cell D four, and I will edit the formula, so that I will start with the sort function. I have my array, which is the result of 166 | the unique formula, that I created earlier, then a comma. My sort index will just be the values. I don't need to define which column it is 167 | within the data set, and the sort order will be ascending. So lowest value first, highest value at the end. So I'll type a one and I don't need 168 | to indicate by column, so I'll just type a right parentheses and enter. And then I get an alphabetical list, of the unique varieties. If I were 169 | to change the value in B four, from variety four to variety five. So I'll do that now and enter, you can see that the formula results are 170 | updated. I'll press control Z, to go back to what I had before. Now let's assume, that I want to create a calculation that will provide a 171 | variable using Let. That calculates the number of unique varieties within a data set. So what I'm doing here, is calculating the percentage of 172 | the four unique varieties, within the nine total cases that I have in the warehouse. So I'll go to cell F four, type in equal sign, and I'll 173 | start with Let, which is the function, that we use to store intermediate calculations in a variable. The name will be Num, N U M, that'll be the 174 | first value. This variable contains the total number, of items in the set, so it'll be nine, then a comma. And the value for this will be to 175 | count, all the non empty cells in the range B four to B 12. So I'll do count A, which counts the number of cells that are not empty, left 176 | parenthesis and then the range is B four to B 12. All right, close out those parentheses in the comma. The second name that we'll use is U N Q, 177 | and that is short for unique, then a comma. And we'll count the number, of unique values within the same range. So that will be count A as 178 | before, left parentheses unique, and the range is B four to B 12. I'll just type that in. Then I have my right parenthesis there. And because I 179 | have unique nested, within Count A I need to type another right parenthesis, then a comma. And then finally the calculation. And that will just 180 | be the number of unique values. So U N Q, divided by the total number of items, and that is N U M. And you can see that both U N Q, and N U M 181 | appeared in the formula, auto complete list. So I'll do that, and then a right parenthesis, and everything looks to be balanced out. So I'll 182 | press enter, and there you have it. It counts all the unique varieties, divided by the total number of items in the set, and gives me a percent 183 | unique of 44. And again, if I edit the value in B four, from variety four to variety five and enter, it goes up to 56%. 184 | 185 | Define a variable using LET 186 | Selecting transcript lines in this section will navigate to timestamp in the video 187 | - A Lambda statement, lets you create, reusable custom functions in Excel. A Let statement, by contrast gives you the ability to define 188 | variables that can be used within those Lambda functions. In this movie, I will show you the first part of the process, which is how to define a 189 | variable using Let. My sample file is, 0 2 0 3 Let, and you can find it in the chapter two folder, of the Exercise Files collection. In this 190 | workbook I have a list of cases and also varieties. So let's say that we're in a warehouse, and I have cases, that contain varieties of seed, 191 | that I want to use on a farm, and they've been distributed randomly. And you see that in column B. Case one contains Variety four, so does 192 | column two. Case three contains Variety three, and you can see the rest. Let's say there are more than four varieties possible, and I want to 193 | have a list of all the unique varieties that actually were selected for this particular case. To do that, I can go to Cell D four and then type 194 | equal. And the function I'll use is fairly new, and that is unique. And this finds the unique values, within a data set. And my array is B four 195 | to B 12. I don't need to change anything else, 'cause I'm just looking for unique values, right Parentheses and enter. And the result of the 196 | formula starts in cell D four, and then spills to cells D five through D seven. So I have four unique varieties. This isn't all that easy to 197 | read though, because they are not listed in alphabetical order. I can change that by using the sort function, which is also relatively new. So I 198 | will double click cell D four, and I will edit the formula, so that I will start with the sort function. I have my array, which is the result of 199 | the unique formula, that I created earlier, then a comma. My sort index will just be the values. I don't need to define which column it is 200 | within the data set, and the sort order will be ascending. So lowest value first, highest value at the end. So I'll type a one and I don't need 201 | to indicate by column, so I'll just type a right parentheses and enter. And then I get an alphabetical list, of the unique varieties. If I were 202 | to change the value in B four, from variety four to variety five. So I'll do that now and enter, you can see that the formula results are 203 | updated. I'll press control Z, to go back to what I had before. Now let's assume, that I want to create a calculation that will provide a 204 | variable using Let. That calculates the number of unique varieties within a data set. So what I'm doing here, is calculating the percentage of 205 | the four unique varieties, within the nine total cases that I have in the warehouse. So I'll go to cell F four, type in equal sign, and I'll 206 | start with Let, which is the function, that we use to store intermediate calculations in a variable. The name will be Num, N U M, that'll be the 207 | first value. This variable contains the total number, of items in the set, so it'll be nine, then a comma. And the value for this will be to 208 | count, all the non empty cells in the range B four to B 12. So I'll do count A, which counts the number of cells that are not empty, left 209 | parenthesis and then the range is B four to B 12. All right, close out those parentheses in the comma. The second name that we'll use is U N Q, 210 | and that is short for unique, then a comma. And we'll count the number, of unique values within the same range. So that will be count A as 211 | before, left parentheses unique, and the range is B four to B 12. I'll just type that in. Then I have my right parenthesis there. And because I 212 | have unique nested, within Count A I need to type another right parenthesis, then a comma. And then finally the calculation. And that will just 213 | be the number of unique values. So U N Q, divided by the total number of items, and that is N U M. And you can see that both U N Q, and N U M 214 | appeared in the formula, auto complete list. So I'll do that, and then a right parenthesis, and everything looks to be balanced out. So I'll 215 | press enter, and there you have it. It counts all the unique varieties, divided by the total number of items in the set, and gives me a percent 216 | unique of 44. And again, if I edit the value in B four, from variety four to variety five and enter, it goes up to 56%. 217 | 218 | Refer to Excel tables in a LAMBDA 219 | Selecting transcript lines in this section will navigate to timestamp in the video 220 | - [Instructor] In many worksheets, you will refer to specific cell ranges in your lambda and lead functions. In other worksheets, you will have 221 | data that is stored in an Excel table. In this movie, I will show you how to use Excel tables as parameters for your lambda and lead functions. 222 | My sample file is oh two oh five Excel tables and you can find that in the chapter two folder of the exercise files collection. In this 223 | workbook, I have a single worksheet and I count the number of unique varieties from a list here on the left that's stored in an Excel table and 224 | then I calculate the percentage of unique varieties. So in other words, I have four separate unique varieties and I have nine items over here 225 | and that leads to a calculation of 44%. The formula in cell F4 that calculates this result is on the formula bar, and you can see that it looks 226 | at the range B4 to B12 twice, counting the total number of values and counting the number of unique values. However, let's say that I want to 227 | add values to the table. That would change the calculation and I want Excel to automatically update the formula. To do that, I can refer to the 228 | Excel table that stores the varieties that are currently in B4 to B12 with the header in B3. So if I click a cell in the table and then go up to 229 | table design and look all the way at the left, I see that the table name is Variety List. So that's the table name that I will use for my cell 230 | reference. I will go back to click F4 and in the formula bar I will edit B4 to B12 to Variety List and then I'll indicate the column by typing a 231 | left square bracket and Varieties is the name of that column and I'll highlight it and press tab and then a right square bracket to close out 232 | the reference and I'll do the same thing within the unique function here. So I have B4 to B12, and that was variety list. Left square bracket 233 | varieties, right square bracket to close it out and enter and we get the same value as a result, but you can see that the references have 234 | changed in the formula. Now if I add a row to the table, then the calculations will update automatically. So I'll click cell B12 and then press 235 | tab to add another row to the table and I currently have unique value of 56% and that's because blank is considered a new value. So I'll type 236 | variety three and enter. So now we've gone down to 40% unique values and that's because I have 10 entries and there are only four unique ones. 237 | If I edit the value in cell B13 from variety three to variety five and press enter, then we have five unique varieties and the percentage unique 238 | is 50%. Now one thing you'll note here is that I have my unique varieties and those are calculated using a formula and the formula result spills 239 | from the original cell of D4 down to, in this case, D8, and it's tempting to try to format this as an Excel table, but in fact you cannot and 240 | I'll show you what happens when you try. So in D4, I will, on the home tab of the ribbon, go up to format as table, and I'll click a style, 241 | whereas the data D4 to D8, my table has headers. I'll click okay and formulas are rich data types will be converted to static text. You want to 242 | continue? No. So in other words, what's happening is that I'm unable to create a table based on this data and have it remain as the result of a 243 | formula. So as you can see, Excel tables let you name your table and columns within them, which makes your formulas easier to read. If you're 244 | able to do so, and especially if you're bringing data into your workbook from an external source, use Excel tables in your formulas so they're 245 | easier for your colleagues to understand and for you to remember. 246 | 247 | Create logical branches using IF and IFS 248 | Selecting transcript lines in this section will navigate to timestamp in the video 249 | - [Narrator] Many calculations you create using LAMBDA will require conditional calculations. For example, you might use one calculation if an 250 | input is less than $500 and another if the input is 500 or more. In this movie, I will show you how to check for conditions using the IF and IFS 251 | functions. My sample file is 03_01_if and you can find it in the chapter three folder of the exercise files collection. There is a single 252 | worksheet in this workbook and on it I have purchase amounts and the goal is to assign points based on those purchase amounts and we'll have 253 | bonuses. We're going to offer 150% points for purchases of more than $500 to start, and we'll change that in a moment. So to create the formula, 254 | I'll click in cell C4, already selected for me, and I'll just go in and create the LAMBDA directly. So I'll do equal LAMBDA. Our only parameter 255 | will be the amount which I will abbreviate as amt and now I can create my IF statement. So I use if, and this is just like the function that we 256 | use normally in workbooks. So that's AMT greater than or equal to 500 than a comma. If that's the case, then the points will be the purchase 257 | amount multiplied by 1.5. So AMT asterisk 1.5 in a comma, and otherwise it's just amt which is the amount of the purchase right parentheses to 258 | close out the if, right parentheses to close out the LAMBDA. And to test, we'll use the value from B4. So I'll use that as an input and 259 | parenthesis sell B4, close it out and enter, and there are the points. And if I drag the formula down I can see that it works properly and that 260 | I have bonus points which are greater than the purchase amount for 510 and 1400. Let's say that I want to add additional conditions and for that 261 | I would use the IFS statement. So I will double click in cell C4 and I'll change the function name to ifs. And this allows me to create multiple 262 | pairs of condition and output. So I have ifs, we'll still use amount, but I'm going to delete everything else I have behind it. I'm going to 263 | start with the most restrictive condition that will be less than 500, comma and that is just the amount of the purchase, so AMT then a comma. 264 | Next is if the amount is less than 1000, so AMT less than 1000, comma. If it is, the amount would be multiplied by 1.5, multiplied by 1.5. And 265 | we're going to add an additional condition where we get double bonus points for any purchase of a thousand or more. So if AMT is greater than or 266 | equal to 1000 which covers all the other possible values then a comma and we're going to multiply AMT by two. Couple of right parentheses to 267 | close out and again, I will use cell B4 as the input test, enter right, 350. Now I should see 765 for cell C5 and 2,800 instead of 2,100 for 268 | cell C6. So I'll go ahead and copy the formula down and those are the results that we expected. Now I can create a LAMBDA based on the formula 269 | that I just created. So I'll double click C4 and I'll copy everything over within the LAMBDA itself, not including the input. Then control C to 270 | copy and escape. Then I'll go to formulas and click the name manager, new, and I'll leave the name as points, that's fine. And then for the 271 | comment I'll say calculate purchase points, including bonuses. And then I will delete the text in refers to and control V to paste what we 272 | created before. Click OK, everything's good and points is the name of the function. So I'll click close and then I'll delete what's in the 273 | cells, C4 to C6 and equal points calculate purchase points, including bonuses. Excellent and the amount is before right parenthesis and enter 274 | 350 is good. And we should see the values that we had before. And we do 765 and 2,800. 275 | 276 | Select a value using CHOOSE 277 | Selecting transcript lines in this section will navigate to timestamp in the video 278 | - The CHOOSE function lets you assign a value to an input. For example, you could assign the name of Monday to a date with a value of one for 279 | its weekday. In this movie, I will show you how to use CHOOSE to assign the text name of a weekday to a date. My sample file is 0302 CHOOSE and 280 | you can find it in the chapter three folder of the exercise files collection. The CHOOSE function selects a value from a list based on an index 281 | number. We have a seven day week in the US. So, that means that the days are numbered one to seven. So I can work with the dates that I have in 282 | column B and get the text name of a weekday based on the dates in that column. So I'll start in column C. Type equal and we're creating a 283 | lambda, so I'll use that function. The input is the date, comma, and then we'll choose from a list that I define based on the index number of 284 | the weekday of the date in cell B4. I do need to be careful though about what value is returned. So I'm going to make sure the return type is 285 | correct. I'll type a comma and I see the first option. Number one has number one for the weekday is Sunday and number seven is Saturday. I tend 286 | to think of Monday as the start of the week. So I will choose option number two. So Monday is number one, and Sunday is number seven. Then a 287 | right parentheses and that closes out weekday then a comma and now I can add in the text values that we'll choose from and there will be seven 288 | of them. Again, corresponding to the weekdays. So in double quotes, because it's text Monday, comma, Tuesday, comma, Wednesday. That one's 289 | always tricky, comma. Then Thursday, comma, Friday, comma. The quotes Saturday and Sunday and double quotes again, and that will close out the 290 | CHOOSE function. And then right parentheses again to close out the Lambda. And I will use the text or rather the date from cell B4. So I'll type 291 | that as an input in a separate set of parentheses and enter. And it worked. October the seventh was indeed a Friday. And I can copy that formula 292 | down. And you see that we have all of the days of the week represented and they all appear to be spelled correctly. Now I can define a function 293 | using this Lambda that I just created. So I'll double click cell C4 and I'll copy everything except the input at the end. So I've selected it. 294 | Control C, escape. Then I'll go to the formulas tab, click the name manager, click new, and for the name, I'll call it weekday text. And for the 295 | comment, return text of a weekday with Monday one and Sunday seven. And then it will refer to the formula that I just copied. So in refers to I 296 | will replace the existing text with what I copied earlier. Click okay. And just remember it's weekday text, W-K-D-A-Y. Click close. Then I'll 297 | select cell C4 through C10 and delete the contents, and with them still selected, type equal. And then weekday text, and the date is then B4, 298 | then right parentheses to close out, and to enter the text into all of these selected cells at the same time, I'll press control enter, and 299 | there we go. We get the results that we expected and they match what we saw earlier. 300 | 301 | Select a value or display a default value using SWITCH 302 | Selecting transcript lines in this section will navigate to timestamp in the video 303 | - The switch function lets you display specific results for specific inputs, but it also lets you display a result for any input that you did 304 | not list. In this movie, I will show you how to handle inputs that don't match your listed values using switch. My sample file is oh three oh 305 | three switch, and you can find it in the chapter three folder of the exercise files collection. Over on the left, I have a set of product 306 | categories and then in cell C4, I have a data validation list that I can use to select from those categories. So, if I click sell C4 and click 307 | the down arrow you see a list of the values from over on the side and I'll switch to batteries just to show you how it works. If you want to 308 | create that kind of list, you can click the cell where you want to create it. Go to the data tab, and then in the data tools group click the 309 | data validation button, select list from the allow box, and then click the collapse dialogue button and select the cells that you want to use as 310 | the data source. And when you're done, click okay and your list will be in place. My goal with this worksheet is to determine whether a discount 311 | is available for a particular purchase category. So in cell E4, I will have that indication and I will use the switch function. So I'll type 312 | equal and I'm going to build it as a lambda directly. So lambda, the parameter will be the category, which I will abbreviate as cat, and then I 313 | can create my switch function. And now I indicate the data that it's going to receive, and that will be cat, which is the lambda specific 314 | variable that I created before. Now the first value, and I'll just go in alphabetical order will be batteries and it's tax, so it's in double 315 | quotes. And I'll say, yes comma, actually, I'll do a quote, 10% and then double quotes, and comma again. And then the next item will be light 316 | bulbs, and I'll make sure that I have it spelled correctly. I do. If you're unsure of your spelling, you can always copy the data from the data 317 | validation list if you have one. Then a comma, and the text for this is yes colon, 15%, then a comma, and now I can have the default value, 318 | which is what will be returned for anything else that's put into cell C4. So I'll put that in double quotes and no discount available, and 319 | double quotes again. And right parenthesis once to close out switch. Right parenthesis again to close out the lambda. And I will use the value 320 | from C4. So I have that in parenthesis. Enter and we get a discount of 10%. And then if I change to solar panels, nope. And then light bulbs 321 | there is. And then power converters, no. So I have all of my cases covered, and if I were to type a value into C4 directly, such as cables and 322 | enter, you can see that my data validation list doesn't allow that to happen. So I'll go ahead and click cancel, and now I can create a lambda 323 | based on what I just created. So I will double click cell E4, and copy everything from the equal sign through the rest of the Lambda, not 324 | including the input. Then control c to copy. Escape. Then I'll go to the formulas tab. Click name manager. I already have my table here called 325 | product categories. That's the list of categories on the left. I want to create a new name, so I'll click new, and for the name, I'll just type 326 | discount. And in the comment check if discount available for category. And then I will delete the text in the refers to box and control v to 327 | paste in the lambda I created earlier. And I'll click okay. So I have discount as my function. I'll click close, and then I will delete the text 328 | in E4 and type equal discount. And the category will be in C4 so I'll click there, right parentheses and enter. No discount available for Power 329 | Converters. But, if I go back to C4 and click batteries, then I do get one. If you're trying to decide whether to use the choose function or the 330 | switch function, remember that the switch function lets you have a value returned in case you don't have a match with your user's input. 331 | 332 | Scenario: Calculate Economic Order Quantity 333 | Selecting transcript lines in this section will navigate to timestamp in the video 334 | - [Instructor] Placing an order with a supplier and holding the inventory you receive cost money. In this movie, I will show you how to create a 335 | Lambda to calculate the economic order quantity for your product, which is the number of items that minimizes your total cost of ordering. My 336 | sample file is 0401EOQ, and you can find it in the chapter four folder of the exercise files collection. This workbook contains a single 337 | worksheet, and on it I have all the information we need to calculate EOQ. Starting out with Setup Cost. And Setup Cost is the total cost of 338 | placing an order, that could be any fees for ordering that your supplier charges you, and also the cost of doing business. That could also 339 | include what you have to pay your employees for their time in creating the order. Flow rate per week is the number of units that you typically 340 | sell. So in this case, we assume that you would sell 1800 of this particular item per week. And finally, your holding cost. That is the cost of 341 | keeping an item in inventory for a week. So that is in the same units as the flow rate. And the equation is over on the right. And how this was 342 | derived is a long story. So I'll just ask you to accept that it is what you need to do to calculate the basic EOQ quantity. So in cell B6, I'll 343 | create the EOQ formula. I'll type in equal sign, and then we're taking the square root of the quantity that we see on the right, that'll be two 344 | multiplied by the setup cost, that's in B3, multiplied by the flow rate, that's in B4, and dividing all of that by holding cost, and that is in 345 | B5. And I don't need to use any extra parenthesis because multiplication and division are on the same level within the order of operations for 346 | Excel. So I can do it this way. If I had a plus in there, then I might need to add parenthesis, but in this case I don't. So I'll type a right 347 | parenthesis here to close out the arguments for square root and Enter. And I get an EOQ of 2939 and a bit, and we'd probably round that up to 348 | 2940. Now with that in place, I can create a Lambda based on that information. So I will click B6 and then I will open it for editing. And then 349 | to the left of square root and to the right of the equal sign, I'll type Lambda. And we're going to be using three different inputs or 350 | parameters. The first one is setup, so that's a setup cost, then a comma. The second is the flow rate. So I'll do flow underscore rate, then a 351 | comma. And finally the holding cost, and I'll just call that hold, then a comma. Now I have my calculation but I need to replace the cell 352 | addresses with the parameters. So two remains the same. B3 is the setup, and you can see that it appears in the formula auto-complete list. And 353 | then B4 is the flow rate. And then B5 is the holding cost or hold. Now I will add a right parenthesis to the end to close out the Lambda, and to 354 | test I'm going to use B3, B4, and B5 as inputs. So I need to put them in a separate set of parentheses. So that's B3, B4, B5. And right 355 | parentheses and Enter. And we get the same answer that we did before. So the Lambda is working. Now I can create a function in this workbook 356 | using the name manager. So I will double click cell B6, and then copy everything for the Lambda itself, but not the arguments to the right. Then 357 | Control+C to copy and Escape. Now I'll go to the formulas tab on the ribbon, click Name Manager, click New and the name is EOQ, that's already 358 | there so I'll just leave it. For the Comment; Calculate Economic Order Quantity, and then in Refers to, I will replace the existing text with 359 | the text that I copied. All right, everything is good, so I'll click OK. And I have EOQ, it's indicated as a Lambda. So I'll go ahead and click 360 | Close. Now, I'll go back and edit the value in cell B6 to equal EOQ, there's the name of the function, left parenthesis, the setup is B3, flow 361 | rate is B4, holding cost is B5 and Enter. And we get the value that we did before. So now you can copy this EOQ custom function to other 362 | workbooks and use them there as well. 363 | 364 | Scenario: Calculate quality of service 365 | Selecting transcript lines in this section will navigate to timestamp in the video 366 | - [Instructor] Most businesses set goals for the quality of service they offer to their customers. One scenario would be a repair company that 367 | sends parts to wind farms in the American Midwest. In this movie, I'll show you how to create a LAMBDA function to calculate the quality of 368 | service given customer demand and warehouse location. My sample file is 04_02_QualityOfService, and you can find it in the Chapter 04 folder of 369 | the Exercise Files collection. Rather than start with the description of the workbook's contents, I'm going to talk about the goal, and that 370 | goal is found here at the bottom right. Let's suppose that for our quality of service, we want to ship parts no more than 250 miles to a 371 | particular customer. Now, that's not going to be possible. Some parts will almost certainly have to go more than 250 miles. So we have set a 372 | goal of sending no more than 70% of all parts more than 250 miles. And you can see that given the current configuration, we have a quality of 373 | service level of 70.02%. So we're meeting our goal, and these numbers were generated with the idea of minimizing cost. So how do we get to those 374 | calculations? Well, at the top left, I have a table that shows the distance between our customers, which are here in column B, and the 375 | warehouses, which are here in row three. And the distance is in miles. And below that, we have a matching array with zeros and ones. Any value 376 | that is greater than 270 gets a zero, and any value that is less than 270 gets a one. So you can see for Amarillo that the first item or 377 | distance is 270. So that gets a zero because it's more than 250. And then the second is 210. So that gets a one. And the same throughout the 378 | rest of the array. On the right we have units shipped. So in this case, we're sending 72 from Amarillo to Abilene. And then if you look down, to 379 | save costs and meet our goal, we had to send three from Amarillo to Dodge City and another 397 from Kansas City. And we're doing that even 380 | though Amarillo is about 90 miles closer to Dodge City than Kansas City. So you always have to make some trade-offs. And at the bottom, we 381 | calculate the quality of service using a SUMPRODUCT formula. SUMPRODUCT multiplies two arrays of the same size element by element and then adds 382 | up the total. So here, we have 72 times zero and then 528 times zero, which means that none of these units count as being below 250 miles. 383 | However, this 325 from Amarillo to Lawton does because 325 is multiplied by one instead of zero. Then, the rest of the formula gets the sum of 384 | all the units shipped, and that's the array here from H2 to J11 and divides that to find the percentage. So our goal is to transfer our current 385 | formula to a LAMBDA. So I'll start by double-clicking in cell H18, and the function will be a LAMBDA. And we'll have two parameters, shipped and 386 | dist, D-I-S-T. So we have the number shipped and then the distance. For the SUMPRODUCT, instead of our ranges, I'm going to use the parameters, 387 | so the SUMPRODUCT of shipped multiplied element-wise and added with D-I-S-T. And then we'll have the SUM of shipped. I will close out this part 388 | of the formula with a right parenthesis. So we have our LAMBDA. And now I need to tell it which items to use, and those'll be the ranges. So 389 | we're using this as an input to make sure that our LAMBDA's working properly. So then we will have H4 to J11. That is the number shipped, then a 390 | comma. And then the distance, whether it's below the maximum or not, is C16 to E23. There we go. Right parenthesis and Enter, and we get the 391 | same value we did before. And now we can turn it into a named function. So I will double-click cell H18 and copy everything through the LAMBDA. 392 | But I'm not going to include the parameters because we won't need them. Then, Control + C and exit. Now, I'll go up to the ribbon and click 393 | Formulas and Name Manager. Click New, and the name will be QOS_Shipping. And I won't put in a comment this time because this movie's running a 394 | little bit long. And then press Control + V to copy the LAMBDA function that I created earlier, and click OK. Right, that looks good. I'll click 395 | Close. And now I'm going to replace the LAMBDA that I have in H18 with a function that uses the ranges that I described earlier. So I will have 396 | QOS_Shipping, and then we just need the range for the units shipped. And that again is H4 to J11, then a comma, and the distances and whether 397 | they are 250 or lower. So that's C16 to E23, right parenthesis, and Enter. And we get our result. 398 | 399 | Scenario: Calculate quality of service 400 | Selecting transcript lines in this section will navigate to timestamp in the video 401 | - [Instructor] Most businesses set goals for the quality of service they offer to their customers. One scenario would be a repair company that 402 | sends parts to wind farms in the American Midwest. In this movie, I'll show you how to create a LAMBDA function to calculate the quality of 403 | service given customer demand and warehouse location. My sample file is 04_02_QualityOfService, and you can find it in the Chapter 04 folder of 404 | the Exercise Files collection. Rather than start with the description of the workbook's contents, I'm going to talk about the goal, and that 405 | goal is found here at the bottom right. Let's suppose that for our quality of service, we want to ship parts no more than 250 miles to a 406 | particular customer. Now, that's not going to be possible. Some parts will almost certainly have to go more than 250 miles. So we have set a 407 | goal of sending no more than 70% of all parts more than 250 miles. And you can see that given the current configuration, we have a quality of 408 | service level of 70.02%. So we're meeting our goal, and these numbers were generated with the idea of minimizing cost. So how do we get to those 409 | calculations? Well, at the top left, I have a table that shows the distance between our customers, which are here in column B, and the 410 | warehouses, which are here in row three. And the distance is in miles. And below that, we have a matching array with zeros and ones. Any value 411 | that is greater than 270 gets a zero, and any value that is less than 270 gets a one. So you can see for Amarillo that the first item or 412 | distance is 270. So that gets a zero because it's more than 250. And then the second is 210. So that gets a one. And the same throughout the 413 | rest of the array. On the right we have units shipped. So in this case, we're sending 72 from Amarillo to Abilene. And then if you look down, to 414 | save costs and meet our goal, we had to send three from Amarillo to Dodge City and another 397 from Kansas City. And we're doing that even 415 | though Amarillo is about 90 miles closer to Dodge City than Kansas City. So you always have to make some trade-offs. And at the bottom, we 416 | calculate the quality of service using a SUMPRODUCT formula. SUMPRODUCT multiplies two arrays of the same size element by element and then adds 417 | up the total. So here, we have 72 times zero and then 528 times zero, which means that none of these units count as being below 250 miles. 418 | However, this 325 from Amarillo to Lawton does because 325 is multiplied by one instead of zero. Then, the rest of the formula gets the sum of 419 | all the units shipped, and that's the array here from H2 to J11 and divides that to find the percentage. So our goal is to transfer our current 420 | formula to a LAMBDA. So I'll start by double-clicking in cell H18, and the function will be a LAMBDA. And we'll have two parameters, shipped and 421 | dist, D-I-S-T. So we have the number shipped and then the distance. For the SUMPRODUCT, instead of our ranges, I'm going to use the parameters, 422 | so the SUMPRODUCT of shipped multiplied element-wise and added with D-I-S-T. And then we'll have the SUM of shipped. I will close out this part 423 | of the formula with a right parenthesis. So we have our LAMBDA. And now I need to tell it which items to use, and those'll be the ranges. So 424 | we're using this as an input to make sure that our LAMBDA's working properly. So then we will have H4 to J11. That is the number shipped, then a 425 | comma. And then the distance, whether it's below the maximum or not, is C16 to E23. There we go. Right parenthesis and Enter, and we get the 426 | same value we did before. And now we can turn it into a named function. So I will double-click cell H18 and copy everything through the LAMBDA. 427 | But I'm not going to include the parameters because we won't need them. Then, Control + C and exit. Now, I'll go up to the ribbon and click 428 | Formulas and Name Manager. Click New, and the name will be QOS_Shipping. And I won't put in a comment this time because this movie's running a 429 | little bit long. And then press Control + V to copy the LAMBDA function that I created earlier, and click OK. Right, that looks good. I'll click 430 | Close. And now I'm going to replace the LAMBDA that I have in H18 with a function that uses the ranges that I described earlier. So I will have 431 | QOS_Shipping, and then we just need the range for the units shipped. And that again is H4 to J11, then a comma, and the distances and whether 432 | they are 250 or lower. So that's C16 to E23, right parenthesis, and Enter. And we get our result. 433 | Scenario: Calculate process capacity given a batch size 434 | Selecting transcript lines in this section will navigate to timestamp in the video 435 | - [Instructor] Many companies create multiple versions of the same product, perhaps changing the flavor of ice cream, or making different colors 436 | of the same product. Because they need to change from one product type to another, companies will divide production into batches. In this movie, 437 | I'll describe how to create a lambda function to calculate the capacity of a process given various constraints. My sample file is 0403 Batch 438 | Size, and you can find it in the chapter four folder of the exercise files collection. This calculation uses three inputs. So I have my batch 439 | size, which is 2,400, so that's the number of items that will be created in one run of this process. The setup time in hours is 0.4, so that is 440 | 0.4 times 60 minutes, so that's 24 minutes. And the processing time per unit, which is also in hours, is 0.05, and that is 0.05 times 60, or 441 | three minutes, 1/20th of an hour. Now we can create the formula that I have written over to the right, and that's the batch size divided by the 442 | setup time, plus the batch size, times time per unit. So we count the setup time once, and then we add the total of the time that's needed to 443 | create all the items. And then we divide the batch size by that quantity. So that gives us our capacity per hour. So in B6, I'll type equal, the 444 | batch size is in B3. Then we'll divide that by the quantity of the setup time, which we only count once, that's in B4. But then we will add the 445 | batch size by the time per unit to get the total time required. So there I have a right parenthesis, everything is good. Enter. And we get a 446 | capacity per hour of 19.9. So just below 20. One way to look at this calculation is that we can make 20 units per hour. So that is the upper 447 | limit for our capacity if there is zero setup time. However, the setup time takes 24 minutes, or four tenths of an hour, and that reduces our 448 | capacity. Because we're going to be running for a while, it means that the setup cost is low in comparison to the overall time required. Now say 449 | that I want to create this calculation as a lambda that we can use here and in other workbooks. So I will double click cell B6, and then I'll 450 | click, to the right of the equal sign and type lambda. We'll use three parameters. That will be batch, comma, and then set up, comma, and then 451 | P-R-O-C for process. And our calculation, and I'll just delete everything I have here. And that will be batch, which again is our batch size. 452 | And that will be divided by the quantity of the setup time plus the batch size, times processing time per unit. And that is PROC. Then I will 453 | add a right parenthesis to the outside to close out the lambda, and I'll provide three inputs in parentheses. Those will be B3 for the batch 454 | size, B4 for the setup time, and B5 for processing time per unit. Right parentheses and enter. And we get the same value as before. And now I 455 | can create a lambda function that we can use in this workbook and copy elsewhere. So I will double click B6, and then copy everything to the 456 | last parentheses of the lambda. Then CTRL+C, then escape. Then on the formulas tab of the ribbon, I'll click name manager, click new, and the 457 | name will be batch capacity. And then I'll skip adding a comment for now. And then I will paste the text that I just copied from the formula 458 | into the refers to box, replacing what's already there. Now I'll click okay, batch capacity is there, so I'll click close. And then in cell B6 459 | I'll test it by typing equal batch capacity. And then we have B3, comma, B4, comma, B5, right parentheses and enter. And we get the value that 460 | we did before. 461 | 462 | Scenario: Clean up imported text 463 | Selecting transcript lines in this section will navigate to timestamp in the video 464 | - [Instructor] When you bring in data from an outside source, such as a scanned PDF file, you might end up with characters you don't want and 465 | spaces everywhere. In this movie, I will show you how to build a lambda function to clean up that text. My sample file is 04_04_Text, and you 466 | can find it in the chapter four folder of the exercise files collection. In column B, I have a set of company names that I brought in from an 467 | outside source. And you can see that I have extra spaces at the beginning and also in the middle of some of these words, also that everything is 468 | lowercase. And in a few cases, I have added spaces at the end, just to make it to be a little bit of a more realistic exercise. I decided not to 469 | include any non-printing characters. And the reason I did that is because you wouldn't be able to see them, but you could see that there were 470 | additional spaces at the end of the value in B3. Because company names typically have capital letters at the start of each of their words, or at 471 | least most of them, you can correct the text in three different ways. So the first will be to clean all the text, that is, to take out the 472 | non-printing characters, then to trim the text, which removes all white space, spaces, tabs, and so on, except for one space between each of the 473 | words, and then also proper, which makes the first letter of each word capital. So I'll show you how I do that in cell C3. I need to start from 474 | the outside in, so I'll type =. The last step that I want in my formula is to change the remaining text so that it's initial caps, so that will 475 | be proper. And then the text will actually be coming from two other functions. The first one is trim, and trim is the function that removes 476 | extra spaces from your data. And then, finally, we'll have clean, and clean removes any non-printing characters from your data. And the source 477 | for this will be cell B3. And then 1, 2, 3 right parentheses to close out each of my nested functions and Enter. And I get Bartley Brothers, and 478 | it looks correct. Now I'll copy that formula down. So I'll click cell C3 and then double-click the fill handle at the bottom right corner. When 479 | I move my mouse pointer over it, I know I'm in the right place when the pointer changes to a black crosshair. So I'll double-click, and it goes. 480 | Now I can look at my data, and everything looks okay except for Addison and Clark. And I see here that the word and also has a capital A. And 481 | that's not surprising because I used proper, and that means that every word will have an initial capital letter. So that means I need to change 482 | my rule a little bit to account for this error. So I will double-click cell C3, and I'll put another function on the outside, and this will be 483 | substitute. And this substitutes one string of text for another. So the first argument is the text, and that is everything here, proper, trim, 484 | and clean. Then I'll click after the trailing right parentheses, then a comma. The old text will be And with a capital A, and it is 485 | case-sensitive, so that's in quotes, and then a comma, and then double quote and. So that will be a lowercase and for Addison and Clark. And if 486 | you're reading ahead, you know that something interesting is about to happen. So I'll type a right parentheses and Enter, and then I'm going to 487 | double-click again to copy the formula down. Addison and Clark is correct, but now Anderson Industries is wrong. And you can see that there's a 488 | lowercase a at the start of Anderson. So that means we need to refine our rule a little bit. So I'll go up to C3. And then instead of just 489 | having and, uppercase A plus and lowercase a, I'm going to add spaces. So space And, and then space and space. Great. So now we're only going to 490 | be changing and if it is a word that occurs on its own, with a space on either side. And remember, we will have exactly one space between all of 491 | our words because we use trim to get rid of the extra white space, so then Enter. Bartley Brothers, no change. It's not affected by the rule. 492 | And now we're going to watch the values in C5 and C7 to see how they do. So double-click the fill handle again. Addison and Clark is correct, 493 | and Anderson Industries is correct. Now I can create a lambda that will clean up this particular version of the text. So I will double-click 494 | cell C3. I will click to the right of the equal sign and type lambda. And then we're going to use our text as an input. So I'll type text and 495 | then a comma, and then I'm going to replace the cell reference of B3 with text. And then I'm going to go to the end of the lambda formula, type 496 | another right parentheses to close it out, and then to test, I will give it the input cell of B3. So left parentheses, our input is B3, and 497 | Enter. Looks good. And then I will click cell C3 and double-click the fill handle, copy down. Everything looks great. So now we can go to the 498 | Formulas tab and use the Name Manager to create our function. So I'll double-click in cell C3 and copy the lambda, but not the input, Control + 499 | C and Exit or Escape, then Formulas, Name Manager, click New. And then the name will be CompName 'cause I know it's always going to be used to 500 | clean up company names. I won't add a comment now, to save time. And then I will select and replace the text in Refers to by pasting in using 501 | Control + V, there's my formula. I'll click OK. Everything looks good for the lambda. I'll click Close. And then in cell C3, which I already 502 | have selected, I'll type =CompName, so that's company name. The text is B3, right parentheses and Enter. Looks good. So I'll click C3, 503 | double-click, and the values appear exactly as they should. One reason I wanted to give you a text handling example is because when you bring in 504 | data from outside sources, it can be very hard to work with it. In most cases, you'll have to do some changes by hand, but if you're creative 505 | with your formulas, and you watch out for accidental replacements, you can go a long way automatically. 506 | 507 | Update values using MAP 508 | Selecting transcript lines in this section will navigate to timestamp in the video 509 | - [Presenter] Some data analysis tasks require you to perform a mathematical operation on a set of numbers and write the results to another cell 510 | range. You can use lambda to define that operation and then apply it using the map function. In this movie, I will show you how to do that in a 511 | scenario analysis worksheet. My sample files 0501 map and you can find it in the chapter five folder of the exercise files collection. As I 512 | mentioned, this workbook contains a scenario analysis worksheet and you can see at the top that I have three cases, best, middle and worst. And 513 | then in cell B3 I have a dropdown that I created using a data validation list. So I can select whether I want to display the best case, the 514 | middle case or the worst case and I'll press escape to get out of that display. I have adjustments for each one so if I were to select best then 515 | the numbers in my base scenario which are in row eight and nine would be multiplied by 125%. If I go to worst case, they'd be multiplied by 75%. 516 | And then the middle case, of course is the numbers themselves multiplied by a 100%. Let's say that I want to use the map function in combination 517 | with a lambda to display the adjusted values in row 13. So below the second set of headings and below the adjusted values top level heading. I 518 | can do that by going to cell B13 which I've already selected, typing equal. And then the formula I'll use is lambda. I need to enter my 519 | parameter or calculation in this case it will be two parameters. So I'll have the range which is the range of values, then a comma then another 520 | parameter that will be the factor. And the factor will be the value in B4. That's the adjustment. And now I can enter the calculation, which 521 | will be range. And you can see it in the auto complete list because I've defined it as an argument within the lambda. And multiply that by 522 | factor factor. There we go. And that's correct. So I will type A right parenthesis, but now I want to give some input values so that Excel is 523 | able to test it. So I'll type A left parenthesis, and we'll use the range of B9 to E9 that contains the original base scenario values, then a 524 | comma, and we're going to multiply them by the value in B4. All right, everything is good. I'll press enter and I get the values. I'm currently 525 | in the middle case at 100%, so I'll go up to cell B3 and I'll change it to the best case. And you can see that everything is multiplied by 125%. 526 | If I go down to cell B13 and click it, you can see the formula as I entered it. And if I go to C13, the formula has spilled from cell B13 over 527 | to B14. One thing to notice is that the formula on the formula bar is displayed in gray so you can't actually edit it here. You have to go back 528 | to the cell in which you entered the original lambda formula and that is cell B13. So our lambda function is working correctly. And now we can 529 | define a custom function using the name manager. So with cell B13 still selected, I will go up to the formula bar and I will copy everything 530 | from equal lambda to the right parenthesis after factor. So I'm leaving out the arguments, so I have that selected. I'll press control C, then 531 | escape to exit the cell. Then I'll go to the formulas tab on the ribbon click name manager, and then I will click new. And then in the new name 532 | dialogue box I will change the name to SCENAdj for scenario adjustment. And then for the comment it will be adjust scenario values by a factor. 533 | And then in the refers to box, I will select the existing text and press control V and paste in the lambda. So then I'll click okay and click 534 | close. And our lambda has been created. Now I will select cells B13 through E13 and delete the contents. Then click cell B13 again and then I'll 535 | type equal and then scenario adjustment. So this is the function that we just created. The range will be B9 through E9, then a comma and the 536 | factor is in cell B4. So I'll click there, write parenthesis, and enter. And you can see that the formula spills over and we have the values 537 | that we have before but instead of this long lambda that we have to edit every time we have a simple compact formula and just to make sure 538 | everything works I'll go back up to cell B3 and select worst everything changed, go back to middle. And we have the values we expect with our 539 | lambda function working as expected. 540 | 541 | Summarize values using REDUCE 542 | Selecting transcript lines in this section will navigate to timestamp in the video 543 | - [Instructor] One common data analysis task is to summarize a set of values using a specific calculation. For example, you could multiply every 544 | value in an array by 1/2, or 0.5, and add up the results of the individual operations. One way to apply more complicated calculations is to use 545 | the reduce function in combination with a lambda calculation, and in this movie I will show you how to do it. My sample file is 05_02_Reduce and 546 | you can find it in the chapter five folder of the exercise files collection. In this workbook, I have a worksheet that has a number of routes, 547 | eight of them, in fact, and then the distance for each of those routes. So all that is obvious enough. In column C, I have the adjusted 548 | distance. And the reason I have that column is because I want to assume this business pays any driver who goes 100 miles or more in a day an 549 | additional 50%. So if you offer them a bonus by mile and they go over 100, then you would multiply the distance traveled by 1.5 to get the 550 | adjusted distance. First, I will calculate the values individually and then show you how to do it in a single calculation using your reduce. So 551 | in cell C4, which I've already selected, I'll type equal and I'll use an if function. So if and then a left parenthesis. And the logical test is 552 | whether the value in B4, that is the distance of this route, is greater than or equal to 100, then a comma. If it is, multiply B4 by 1.5, and if 553 | not, we'll just return B4. So everything's good. I've got a right parenthesis, enter, and we get 156. I'm going to copy that formula down. So 554 | I'll click cell C4, and then at the bottom right corner of the cell you can see a green square. That's the fill handle. So I'll move my mouse 555 | pointer over top of it and when it changes to a black cross hair, I will double click, and there we go. So any value that is 100 or more is 556 | multiplied by 1.5. Anything that is 100 or less is just the original value. And I have a formula that gets the sum of all those individual 557 | values here. But now let's suppose that you want to do all of this in one formula in one cell instead of adding up all of the adjusted distances 558 | in a separate column. To do that, you can use the reduce function. So I will go to cell C14 and then equal, and then I'll use reduce. We start 559 | by adding the initial value, and you can leave this blank. If you do, Excel will assume it's zero, but I like to put it in just so the formula 560 | is easier to read. So zero, and then a comma. The array is B4 to B 11, and, remember, we are using the distance and not adjusted distance. If 561 | you multiply the values in adjusted distance by 1.5, then you would be multiplying any value of 100 or more twice. So we have B4 to B 1, and 562 | then a comma. And now we can enter the lambda function. So I will do lambda, and then we'll start with the accumulator value, which will be A, 563 | then a comma. Then we'll use the distance, which I will call DIST, and this is a variable, as the same as A that are internal to this lambda, 564 | then a comma, and now we can create the same "if" function or formula that we did before, except we'll use the values A and DIST. So if, left 565 | parenthesis, logical test is if the distance, DIST, is greater than or equal to 100, then a comma, then we will have the accumulator value, or 566 | A, plus DIST times 1.5, then a comma. And if not, it'll just be A, the accumulated value, plus the distance, DIST. And then I need one, two, 567 | three right parentheses to close everything out. I'll press enter and we get the same value that we did before of 862.5, except that now it is 568 | all contained within one cell. And if I wanted to, I could take the lambda that I used here and go through the name manager to create a named 569 | function that I could use elsewhere. 570 | 571 | Calculate intermediate values using SCAN 572 | Selecting transcript lines in this section will navigate to timestamp in the video 573 | - [Narrator] The SCAN function, which I'll describe in this movie applies a LAMBDA to a data set and calculates intermediate values as it works 574 | through that set. One common example for this type of calculation is a running total which I will demonstrate in this movie. My sample file is 575 | 05_03_scan, and you can find it in the chapter five folder of the exercise files collection. In this workbook, I have a single worksheet and I 576 | have a table of eight routes that could be driven by employees of a particular company and then the distance in column B for each of those 577 | routes. And the idea is that this company offers a bonus to its drivers for the number of miles driven in a day. If the number of miles is 100 578 | or greater then it multiplies the total by 1.5. If not, in other words, if it's below 100 then they just pay the driver for the actual miles 579 | driven. I will use the SCAN function to create formulas to calculate both the adjusted distance and an adjusted running total. So I'll start 580 | with just the adjusted distance. I'll go to cell C4 and then type in equal sign and I will use the SCAN function. The initial value for the 581 | accumulator is zero so we'll start there, then a comma, the array of values that we're going to summarize is in cells B4 through B11, then a 582 | comma and the function we'll use is a LAMBDA. So I'll go with the LAMBDA and we'll use two parameters. The first will be the accumulator value 583 | which I'll just call A then a comma, And then the distance, which I will call dist and A and dist are variables that are used only within this 584 | LAMBDA function. So they're not used anywhere else in the workbook. So then a comma, and now we can put in the calculation. So for this we'll 585 | use an IF function. So if the distance, dist is greater than or equal to 100 then a comma, we'll multiply the distance by 1.5 and a comma, and 586 | if it's below 100, we'll just return the original value. And I need 1, 2, 3, right parentheses, and then I'll press enter. And there we get our 587 | values. And you can see that the formula spilled to include every cell in a row that is next to the cells in the original range of B4 through 588 | B11. And we get our adjusted distance total at the bottom of 862.5. Now let's say that I want to calculate an adjusted running total. So instead 589 | of having the value for each individual route I want to have the accumulated total as we move down through the routes. So I'll go to cell D4 and 590 | this function will be very similar to the previous one. So I'll move through it a little more quickly, only explaining the differences. So in 591 | D4, I'll type equal, and again we'll use SCAN initial value zero range B4 to B11, then a comma, and we're doing a LAMBDA again. And the first 592 | parameter will be A for the accumulator then a comma and distance. And you can see that dist doesn't show up in the auto complete list. That's 593 | because, again it's not something that applies to the entire workbook it's just within this function, then a comma, and now the calculation. 594 | Again it will be an if then left parenthesis if the distance is greater than or equal to 100, comma, if it's true, then we will multiply the 595 | distance by 1.5 and add that to the accumulator value, then a comma and if it's not true then we will just have the distance, unadjusted added 596 | to the accumulator. So everything is good here. So 1, 2, 3, right parenthesis to close out and enter. And there we see we get our adjusted 597 | running total. And I have 156 plus 53 is 209, that's correct. Plus 195 is 404 also correct. And at the bottom the last cell gives me an adjusted 598 | running total of the entire set for 862.5. And that means that I don't have to add up all the values because the formula that I created has done 599 | that for me already. Generate an array of values using MAKEARRAY 600 | Selecting transcript lines in this section will navigate to timestamp in the video 601 | - [Instructor] Data analysis tasks often use array of values to generate results. In this movie, I will show you how to use the MAKEARRAY 602 | function in combination with a LAMBDA to generate a random set of values. My sample file is o5_o4_MakeArray, and you can find it in the chapter 603 | five folder of the exercise files collection. Let's say that I have a goal to plant crops randomly in a field, and in rows three and four, you 604 | can see that I have my number of rows in B3 and the number of columns in B4. And my goal is to plant four varieties randomly, allowing repeats 605 | within a three by three grid. And I can do that using the MAKEARRAY function. So I'll click on cell D3, type an equal sign and we'll use 606 | MAKEARRAY. The number of rows is the value in B3. The number of columns is in B4 and know they do not have to be the same number. Now I can 607 | create my function as a LAMBDA, so I'll do LAMBDA and the first parameter will be the number of rows. So that will be called R, then a comma C 608 | for columns, then a comma. And now I want to choose, let's say from among four varieties and I'll just call them varieties one through four. I 609 | will use CHOOSE, to select a variety at random. The index number will be generated randomly, so I'll use RANDBETWEEN, and we have four different 610 | varieties, so I'll type one as the bottom number than a four as the top number. Close that with the right parenthesis, and then a comma and now 611 | a list of the varieties. Those will be variety one through variety four and I'll type them in with the names between double quotes. So double 612 | quote variety one, and I'm separating each value by commas. Then double quote variety two, and a comma variety three as before, and comma then 613 | variety four and a double quote. And I have to use double quotes, because the values are text and I have spaces, so that indicates that I have 614 | text, right? Everything's good. So I will type one to three right parenthesis to close out and enter. And there I have my list of varieties. If 615 | I want to recalculate it, I can press F9. And that gives me different layout, F9 again. And you can see that some values are changing and some 616 | values aren't. So, I don't have these values changing in the background. While I do my next task, I will select cells D3 through F5, press 617 | control C, and then I will paste in the values using a set of keyboard shortcuts. So, I'll press alt that opens up the key tabs, then H for the 618 | home tab, V four paste and then V again to paste values. So the formulas are gone in cells D three through F5 and I just have the values, so 619 | they'll stay the same. Another task that I wanted to show you that isn't actually related to LAMBDA but is very useful and fits in here. So, I 620 | thought I would go ahead and do it. And the idea is to generate a random ordering for the values in cells A8 through A16. And I can do that by 621 | creating a set of random decimal values and then sorting based on them. So, I'll click on cell B8 and actually drag down to include the range 622 | from B8 to B16. Then in B eight we'll type equal. And then the function is RAND, R A N D. And it doesn't take any arguments, so I'll just type 623 | an open and close parenthesis. And then to enter the same formula in every selected cell, I'll press control enter. And there I have a set of 624 | random decimal values. And if I increase the size of column B, you can see that there are quite a few decimal values displayed. And if I go to 625 | the home tab and the number group and click increased decimal, you can see that there are a lack actually a lot of decimal places. And see if I 626 | increase. Now I believe I finally got to the point where I get repeating zeros. Yes, I have. So if I decrease decimal, and there we go. So that 627 | is how many digits are randomly generated and you will probably, and in fact I'm willing to guarantee that you will never see two values the 628 | same consecutively or even within the same data set. So I want to sort based on these values and I will copy them. So I'll press control C and 629 | paste them in. So alt H, V, V as I did before, and those key strokes are independent, so you don't hold down alt and then press H. It's alt 630 | release H, release V, V. So there are my values, and now I can select the range from B8 through A16. Then again, on the home tab, I'll go to 631 | sort and filter and I will sort smallest to largest. So, I have sorted based on the values that were in the first column I selected. That's why 632 | I selected column B values first. And so I have all those values and you can see that they go in ascending order. And over here I have a random 633 | set of varieties from the values that I created before. So, if I want to create a randomly ordered set of values, all I need to do is type them 634 | in, generate random decimal numbers and then sort based on the random values. 635 | 636 | Apply a LAMBDA to an array by column using BYCOL 637 | Selecting transcript lines in this section will navigate to timestamp in the video 638 | - [Instructor] Lambda functions can be applied to entire arrays of data or just parts of them. In this movie, I will show you how to apply a 639 | Lambda to individual columns of an array. My sample file is 0505 by column, and you can find it in the chapter five folder of the exercise files 640 | collection. This worksheet summarizes the number of packages handled by a company and it's broken down both by day. So you see, I have the days 641 | of the week, Monday through Friday, and also by route. I also assume that I pay my drivers and employees based on the number of packages that 642 | they handle. And if they handle 500 or more on a particular day then I want to give everyone a bonus per package of 110%. So, instead of paying 643 | them say a dollar per package, I would pay them $1.10. And I can calculate that using a Lambda. So, I'll start with just creating a Lambda that 644 | will perform the calculation for a single column. So, I'll click in cell B10, type equal and the Lambda that I create will use the column of 645 | data. So, I'll just type in column as my argument and a comma. And then if the sum of column, which is the data that's been entered is greater 646 | than or equal to 500 comma, then the value if true will be sum of column times 1.1. And if not, it'll just be the total. So comma sum of column. 647 | And three right parentheses to close out. I'm using the colors to know when I get to black which indicates that I've reached balance in my 648 | equation with parentheses and enter and I get calc, and that's because I forgot to tell Excel and the Lambda which range of cells to use. So, I 649 | will double click cell B10, go to the left of the formula, and then left parenthesis, and then we're using B5 through B9. So, that's the cell 650 | range there and enter. And now I get my value. And I can copy this formula to the right. So, I will click cell B10 and then drag its fill handle 651 | to the right and it's been applied. So, any value below 500 remains the same. Any value that started as greater than 500 is now multiplied by 652 | 1.1. So, I've shown that the Lambda works, but now I'm going to change it so that I work on the entire array of data and go by column. So, I 653 | will erase the formulas in cells C10 through F10 so I've selected them. Just press delete and then I will double click cell B10 and start 654 | editing. Here I want to go by column. So, I will put the BYCOL function to the left of Lambda and the range that I want to use will be B5 655 | through F9. So, those are all the values in the array. Then I'll type a comma, all right, everything has been selected. So, I have B5 through B9 656 | and now I want to delete the range at the end so I'm not operating on B5 through B9 nine anymore. And also, I can tell by the colors of the 657 | parenthesis, the right parenthesis here at the end, that I need a black parenthesis. So, if you see here at the start on the left I have a black 658 | left parentheses and then the second one is red, and then you work through the colors and then they balance out. So green and green, green and 659 | green again, green and green. And then we go to the outer areas where we have purple, red, and that means I need black, so everything is 660 | balanced. I'll press enter and I get the same formulas that I did before except now they've been entered with only one formula entry into cell 661 | B10. One other thing that I can do to clean up the data is to round up to the next highest integer value and that uses the round up function. 662 | So, I'll double click B10 again and then to the left of BYCOL and to the right of the equal sign I'll type roundup, left parenthesis, and then 663 | we're rounding up the value generated by the formula here. And then we need to tell it the number of digits to the right or left of the decimal 664 | point. So, I want it to be a whole number and that will be zero digits and then right parentheses to close out and balance out the parenthesis 665 | and enter. And there we get our whole numbers where any fraction or decimal value has been rounded up to the next highest number. 666 | 667 | Apply a LAMBDA to an array by row using BYROW 668 | Selecting transcript lines in this section will navigate to timestamp in the video 669 | - [Instructor] Lambda functions can be applied to entire arrays of data or just parts of them. In this movie, I will show you how to apply a 670 | Lambda to individual rows of an array. My sample file is 05 06 by Row, and you can find it in the chapter five folder of the exercise files 671 | collection. I'm using the same data that I used in the previous movie, but this time I want to focus on the rows, which calculate or summarize 672 | the number of packages that have been delivered on a route for a given week. So I have route one in row five, route two in row six and so on. 673 | I'm assuming that I am paying my employees per package and I want to offer them a bonus of 10% if they handle more than 500 packages on a route 674 | within a week. And that will be the calculation that we do in column G for adjusted route total. So in cell G5, I'll type in an equal sign, and 675 | we're creating a Lambda function. The parameter will be the values in a row, so I'll just use row as that name. And if the sum of the values in 676 | the row are greater than or equal to 500, we'll multiply that sum by 1.1. So I'll have sum, row times 1.1, and if not we'll just return the same 677 | value. So that'll be sum of row. Now I need to close out my parentheses. So there's one, I can see that it's red, and I need another for black. 678 | And you can see that the first left parentheses is black in color, and the second is red, and then it comes out at the other end. So we have 679 | purple and then red and then black. That's how you know you're balanced out. The values were summarizing are in cells B5 through F5, so I'll 680 | input those as arguments. So another left parentheses, and then B5 through F5. And right parentheses looks good and enter. And there we get a 681 | total of 456, and it's under 500, so it wasn't multiplied. And then I'll just go ahead, and copy this formula down. And I can see the three of 682 | the five routes handled more than 500 packages in this week. If I don't want to copy the formula, then I can use the by row function. So I will 683 | delete the formulas in G6 through G nine, and then double-click cell G5 to edit the formula. And here I will use the by row function. So I'll 684 | click just to the right of the equal sign and type by a row. Then a left parenthesis. The array that we're going to use is B5 through F9. So all 685 | the data then a comma, and I don't need B5 through F5 again. So I'll go ahead and delete that. And I see that my parenthesis are out of balance 686 | so I'll type another right parenthesis. Looks good and enter. And there we get the same results as before, but only entering a formula in one 687 | cell. One last thing to do would be to round up the values and we'll give our employees the benefit of rounding up to the next highest number, 688 | even if the decimal value is less than 0.5. So I'll double-click G5 again, and we will use the round up function. So round up, left parentheses, 689 | and then I can keep the rest of this in place because that's the result or number that we're rounding. Then click to the right of what is now 690 | the final red parentheses, then a comma. And we need to indicate the number of digits to the left or right of the decimal point. I want to use 691 | whole numbers, so that will be zero. So we have no digits to the right of the decimal point, right parenthesis and enter. And we get our results 692 | rounded up to the next whole number. 693 | 694 | Manage LAMBDA output 695 | Selecting transcript lines in this section will navigate to timestamp in the video 696 | - [Instructor] Lambda functions can return more than one value. They can be in rows, in columns, or in arrays. In this movie, I will show you 697 | four functions that you can use to manage output from Lambdas that return multiple values. My sample file is 05_07 manage output and you can 698 | find it in the chapter five folder of the exercise files collection. I have two sets of values that I work with in this movie in the range B3 to 699 | D4. I have a number of randomly selected varieties and then I will perform other operations wrapping values into arrays using columns or rows in 700 | the table here from A7 through A13. But I'll start working with the values here in my upper range. My goal is to output this data as a single 701 | row. And what I want Excel to do is to take the first row of data, so that would be row three and then to the right of it, use the data from row 702 | four of the worksheet. So I'll click and cell G2, type equal. And the function I'll use is in cell F2 that is two row and the array is B3 703 | through D4. And I don't need to set any of my other parameters here. So I'll type right parenthesis and enter and I get the values. So you can 704 | see that it takes the first row and then immediately to the right of that, it writes the second row. In the same way, you can write an array 705 | into a single column. So I'll click and cell G4, type equal. And this time, as you might have guessed, the function is TOCOL or to column and my 706 | array is the same as before, B3 to D4. Don't need to change anything else. So I'll type right parenthesis and enter. And there I have variety 707 | four, variety of six and variety one, and then variety three, variety two and variety three. Notice that it did not go by columns. Instead it 708 | went by rows. So it took row one, rendering it as a column and then did the same for row two. You can also go in the other direction. So if your 709 | Lambda returns a single row or a column of data, you can wrap it into an array. So let's work with the data that I have in A8 through A13. So, 710 | and cell C8, I will start typing my formula. So equal, and the function name is wrap columns. The vector and vector is a fancy word for a single 711 | column or row will be the values in this table here. So I'm going to move my mouse pointer over the center of the header of my table. And when 712 | it's a black downward pointing arrow, I will click. That selects the entire table column and that's variety list varieties, then a comma and my 713 | wrap count will be the number of rows that I want. So I will wrap it into three rows. I don't need any padding, so type right parentheses and 714 | enter. And I get three rows and two columns. I can do the same thing for rows. So I'm going to wrap the rows by using =WRAPROWS. The vector is 715 | once again my varieties, then a comma and the wrap count will be three. Right parenthesis and enter. And you can see that I get three columns 716 | spread out over two rows. Now let's see what would happen if I did have a number of varieties that did not fit easily into the array that I 717 | created. So to demonstrate that, I'll go to cell A13, click it, press tab, and then variety seven and enter. And you can see that I have errors 718 | here, N/A and N/A and also the same for wrap rows. And that's because I don't have any values to go there. So it says it's not available. To fix 719 | that, I can edit the original formula. So I will double click cell C13. And then after the three, indicating where I want to wrap my rows, I 720 | will type a comma, and then I'll enter the string empty. So that will be empty in double quotes and enter. And I have the error fixed. So I have 721 | the varieties that you see here and then the last two cells are empty. And just for completeness, I'll go up to the top and then edit the 722 | formula in cell C8, have an empty string again and enter. And there we go. So as you can see, you can use wrap columns and wrap rows to create 723 | an array out of a single vector, either horizontal or vertical. And in the same way, if you have an array of data that you want to change to a 724 | row or column, you can use TOROW or TOCOL to make that happen. 725 | 726 | Troubleshoot LAMBDA output 727 | Selecting transcript lines in this section will navigate to timestamp in the video 728 | - [Instructor] Like most other Excel functions, Lambda can accept single cells or cell ranges as input. However, different types of ranges can 729 | cause interesting problems. So in this movie, I will demonstrate one surprising behavior in Excel and describe a way to work around it with the 730 | goal of giving you an approach to solving problems when you encounter them in your own work. My sample file is 05 08 Troubleshoot, and you can 731 | find it in the chapter five folder of the Exercise Files Collection. Now, before I get started, I want to point out that this is not something 732 | that is unique to Lambda, Instead, this is something that I discovered when I was creating a previous movie, and I thought it was interesting 733 | enough to share. So my goal is to take the data, and the variety list table here, and to count the number of unique items, and to tell you 734 | what's going to happen. There are seven unique values. So I have nine values in this table, and three repeats and eight repeats. Otherwise there 735 | are no other repetitions. So there will be seven unique values. So to count the unique items in the table column there, I will type equal and 736 | then count A. This function counts the number of cells in a range that are not empty, so not just numbers. So I have count A, and then I want to 737 | find the number of unique values. So I will use the unique function, and my array is my table column here. So I will move my mouse pointer over 738 | its header, and when it's a downward-pointing black arrow, I'll click to select. There it goes. And I don't need to change anything else. So I 739 | will type two right parenthesis to close out and enter, and I get seven unique items, so that's correct. I have the same data laid out as an 740 | array in the range D4 through F6, and I want to show you an interesting behavior. So if I create the same type of formula, so I'll have equal, 741 | and that would be count A again and then unique. And the array is D4 through F6, and then close out with two right parentheses and press enter. 742 | I get the number nine, even though three repeats and eight repeats. So that is an interesting behavior that I discovered when I was preparing 743 | another movie in this course. So the question is, "How do you get around it?" Well, it turns out that if you within the formula represent the 744 | data as a single column, then Excel counts them accurately. So I'm going to edit the formula in I6 and instead of just counting the unique 745 | items, I'm going to instead use two call, which changes the data configuration from an array to a single column, a single vertical column. So I 746 | will type a right parenthesis, and then two column. And then I need another right parenthesis at the end to close it out and enter, and it 747 | counts it accurately. All right, so it works with columns. I was wondering if the same thing would work with rows. So I will edit the two-column 748 | function to two row, right? All my parentheses are still good, so I'll press enter and it does not work. But if I press Control + Z to go back 749 | to two column, then it does. So this is interesting, and again, you probably won't have the specific problem, but you might find that you aren't 750 | able to get the result that you want from an array. If that is the case, try transforming the data into a single column using two-column TOCOL 751 | to see if that fixes it. 752 | 753 | 754 | 755 | 756 | 757 | --------------------------------------------------------------------------------