I have some data like below,
UserID | UserName | skills |
1 | John | 1,2,3,4,5|
2 | Mary | 1,2,3|
Can anyone help me with a macro which can change the data structure into:
UserID | UserName | skills |
1 | John | 1 |
1 | John | 2 |
1 | John | 3 |
1 | John | 4 |
1 | John | 5 |
2 | Mary | 1 |
2 | Mary | 2 |
2 | Mary | 3 |
Thank you!
I've just had a minute to make this code for you. Some additional assumptions in comments below.
Sub qTest()
'assumptions:
'1. you need to select top left cell of your original data table, _
i.e. cell UserId
'2. table will be created to the right- there must be empty area
'select UserID cell
Dim i As Long
Dim tmpSkills As Variant
Dim tmpRow As Long
Dim iSkills As Long
Dim tmpArray As Variant
tmpArray = Selection.CurrentRegion
'copying
Selection.Resize(1, 3).Copy Selection.Offset(0, 4)
For i = 2 To UBound(tmpArray)
tmpSkills = Split(tmpArray(i, 3), ",")
iSkills = UBound(tmpSkills) +1
'skils
Selection.Offset(1 + tmpRow, 6).Resize(iSkills, 1) = Application.Transpose(tmpSkills)
'UserId
Selection.Offset(1 + tmpRow, 5).Resize(iSkills, 1) = tmpArray(i, 2)
'UserName
Selection.Offset(1 + tmpRow, 4).Resize(iSkills, 1) = tmpArray(i, 1)
tmpRow = tmpRow + iSkills
Next
End Sub
Picture presenting data before (on the left) and after (on the right). UserID cell should be selected before you run macro.
You can use the text to columns function in Excel.
Please refer to this link:
Microsoft Support
This method looks at each row and then inserts rows and spreads the information in place, overwriting. But I think I like KazJaw's better.
Sub Spread_Skills()
'Spread string of skills down spreadsheet for each UserID
'Application.ScreenUpdating = False 'Uncomment for large files
i = 2
Do While Not IsEmpty(Cells(i, 1)) 'as long as there is a userid do this
If Not InStr(Cells(i, 3), ",") = 0 Then 'if there is a comma, more than one skill, do this
UserId = Cells(i, 1) 'gather info
UserName = Cells(i, 2) 'gather info
adn = Len(Cells(i, 3)) - Len(Application.WorksheetFunction.Substitute(Cells(i, 3), ",", "")) 'count number of skills
Rows(i + 1 & ":" & i + adn).Select 'go to the next row
Selection.Insert Shift:=xlDown, CopyOrigin:=xlFormatFromLeftOrAbove 'Insert a row for each skill-1
temp = Mid(Cells(i, 3), Application.WorksheetFunction.Find(",", Cells(i, 3), 1) + 1, Len(Cells(i, 3))) 'asign string of skills
Cells(i, 3) = Left(Cells(i, 3), Application.WorksheetFunction.Find(",", Cells(i, 3), 1) - 1) 'make the first row the first skill
For o = i + 1 To i + adn 'for each additional skill do this
If Not InStr(temp, ",") = 0 Then 'if it isn't the last skill do this
ntemp = Left(temp, Application.WorksheetFunction.Find(",", temp, 1) - 1) 'slice
temp = Mid(temp, Application.WorksheetFunction.Find(",", temp, 1) + 1, Len(temp)) 'reasign remaining skills
Else: 'if it is the last skill do this
ntemp = temp
End If
Cells(o, 1) = UserId 'enter data
Cells(o, 2) = UserName 'enter data
Cells(o, 3) = ntemp 'enter data
Next o 'next row in skill range
End If
i = i + adn + 1 'go to the next userid
Loop
'Application.ScreenUpdating = true 'Uncomment for large files
End Sub
Related
I have 3 worksheets (user1, user2, result). Each sheet has three columns (A: System_ID, B: Comment, C: Last Modified Time).
The code does this:
Gets maximum last modified time between user1 and user2 in column c.
The result is to get that comment in column b (adjacent to max time found in col c)
put the result (comment) in column b in resut sheet
Simply the comment with last modified time wins and gets pasted in result WS.
Anyways, my problem is that I only can index-match if both indexes in column A have the same sorting/order.
I need to match all records in column A even if they have different row.number or row index.
How to index-match no matter the order in column A
Sub Get_LastModified_Here()
Application.EnableEvents = False
Dim Location1 As Workbook
Set Location1 = GetWorkbook("C:\Users\HP\Desktop\User_1.xlsb")
Dim Location2 As Workbook
Set Location2 = GetWorkbook("C:\Users\HP\Desktop\User_2.xlsb")
Dim SourceCell As Range, SourceRange As Range, CurrentRange As Range
Dim rngTarget As Range
Dim strAdr As String
Dim vSource As Variant, vTarget As Variant, vCurrent As Variant
Dim i As Long
Set SourceRange = Workbooks("User_2.xlsb").Sheets("Data").Range("A2:" & "A1607")
With SourceRange
Set SourceRange = .Resize(.Rows.Count, .Columns.Count + 3)
End With
strAdr = SourceRange.Address
Set rngTarget = Workbooks("User_1.xlsb").Worksheets("Data").Range(strAdr)
Set CurrentRange = ThisWorkbook.Worksheets("Data").Range(strAdr).Offset(0, 1)
vSource = SourceRange
vTarget = rngTarget
vCurrent = CurrentRange
For i = 1 To UBound(vSource, 1)
'Match Column A
If vSource(i, 1) = vTarget(i, 1) Then
'Check max time in Column C (user1 vs user2)
If vSource(i, 3) > vTarget(i, 3) Then
'Get max comment from ((user max)) in column B (result ws)
vCurrent(i, 1) = vSource(i, 2)
ElseIf vSource(i, 3) < vTarget(i, 3) Then
vCurrent(i, 1) = vTarget(i, 2)
ElseIf vSource(i, 3) = vTarget(i, 3) Then
vCurrent(i, 1) = vSource(i, 2)
End If
End If
Next i
SourceRange = vSource
rngTarget = vTarget
CurrentRange = vCurrent
Application.EnableEvents = True
End Sub
Here is a detailed explanation of the issue (I apologize for CAPS letters):
User1 Sheet
I have the SYSTEM_ID in ## Row 1 ##
System_ID
Comment
LastModTime
ID_1
User1 notes
09/12/2020 10:00:01 PM
User2 Sheet
I have the SAME SYSTEM_ID in ## Row 2 ##
System_ID
Comment
LastModTime
ID_1
User2 notes
09/12/2020 10:00:02 PM
This is what I GET in Result Sheet
I have the SAME SYSTEM_ID but in ## Row 3 ##
System_ID
Comment
LastModTime
ID_1
This is what I Want in Result Sheet
I have the SAME SYSTEM_ID but in ## Row 3 ##
System_ID
Comment
LastModTime
ID_1
User2 notes
09/12/2020 10:00:02 PM
What our codes CAN do
Get the comment based on the last modified time, ONLY IF "ID_1" is on THE SAME ROW #. i have tried it (didn't work)
What our codes CAN'T do
Get the comment based on the last modified time, EVEN IF "ID_1" is on A DIFFERENT ROW #. this is where I need help?
EDIT to confirm that the assumptions match:
Sheet User1:
| Id | Comment | LastMod |
| --- | ------- | --------|
| 3 | S1 Comm3| 2 |
| 2 | S1 Comm2| 8 |
| 1 | S1 Comm1| 6 |
Sheet User2:
| Id | Comment | LastMod |
| --- | ------- | --------|
| 1 | S2 Comm1| 3 |
| 2 | S2 Comm2| 4 |
| 3 | S2 Comm3| 8 |
Expected Output:
Id
Comment
NOTES
1
S1 Comm1
Id 1 highest mod is on sheet 1
2
S1 Comm2
Id 2 highest mod is on sheet 2
3
S2 Comm3
Id 3 highest mod is on sheet 3
One option is to build up the result set into a separate collection, and then populate your result set when finished. Since this is an operation that involved multiple lookups (checking to see if a system Id has already been visited), I like to use dictionary objects. These offer highly performant lookup operations.
I'm going to post a much simplified example below that you can hopefully use for your purposes. The code below assumes that the SystemId column is a unique key that maps an entry in sheet1 to an entry in sheet2. It also assumes that each systemId appears once per sheet. If not, it can be tweaked to support that.
The code basically loops through the range and checks if the rows in both sheets have matching system ID. If so, it adds that row to the dictionary, using the ID as a key, and a two element array containing the comment and the last mod time.
If they don't match, it checks each entry against the dictionary to see if that systemID was already visited (earlier on the other sheet). If so, it compares the entries and keeps the most recent mod time, otherwise, it leaves it as is.
Try to work through it and let us know if you need additional help.
Sub Tester()
Dim oDict As Object
Dim a(0 To 1)
Dim sUser1 As Worksheet
Dim sUser2 As Worksheet
Set oDict = CreateObject("Scripting.Dictionary")
Set sUser1 = Sheets("User1")
Set sUser2 = Sheets("User2")
'Here I will assume that both ranges will always
'be the same length. I'm also hardcoding in the
'needed rows. You can use whichever logic
'works best for you to determine how to capture
'all rows in both sheets
For i = 2 To 8
'Two possibilities here:
' 1. The SystemId in both sheets match and
' can be directly compared
' 2. They differ and will each be checked
' to see if they already exist in the dict.
'You can bypass this and just treat each of the
'ranges individually, but I think it would be
'slightly more performant the way I'm doing it.
'
'Also, this assumes that each SystemId will only
'appear once in each sheet, and is a true Primary Key
If sUser1.Cells(i, 1).Value = sUser2.Cells(i, 1) Then
If sUser1.Cells(i, 3).Value > sUser2.Cells(i, 3).Value Then
MergeEntryToDictionary oDict, sUser1.Cells(i, 1).Value, _
sUser1.Cells(i, 2).Value, sUser1.Cells(i, 3).Value
Else
MergeEntryToDictionary oDict, sUser2.Cells(i, 1).Value, _
sUser2.Cells(i, 2).Value, sUser2.Cells(i, 3).Value
End If
Else
'In case they don't match, check each entry against the
'dictionary to see if the systemId has already been added.
'If not, then add it. Otherwise, compare the last mod date
'of the entry to the current, and update if needed.
MergeEntryToDictionary oDict, sUser1.Cells(i, 1).Value, _
sUser1.Cells(i, 2).Value, sUser1.Cells(i, 3).Value
MergeEntryToDictionary oDict, sUser2.Cells(i, 1).Value, _
sUser2.Cells(i, 2).Value, sUser2.Cells(i, 3).Value
End If
Next i
'Below prints back to sheet
Dim k As Variant
Dim n As Long
n = 2
For Each k In oDict.keys
Sheets("result").Cells(n, 1).Value = k
Sheets("result").Cells(n, 2).Value = oDict(k)(0)
Sheets("result").Cells(n, 3).Value = oDict(k)(1)
n = n + 1
Next k
End Sub
Function MergeEntryToDictionary(ByRef oDict As Object, _
SystemId As String, _
sComment As String, _
LastModTime As Double) As Boolean
Dim a(0 To 2)
If oDict.exists(SystemId) Then
If LastModTime > oDict(SystemId)(1) Then
a(0) = sComment
a(1) = LastModTime
oDict(SystemId) = a
End If
Else
a(0) = sComment
a(1) = LastModTime
oDict.Add SystemId, a
End If
MergeEntryToDictionary = True
End Function
am trying to resort the data using Code consider the data shape like this :
Empid| 1/01/2019|2/01/2019 | 3/01/2019
-------------------------------------------
1 | A | B | A
2 | B | A | B
3 | B | C | C
4 | A | A | A
and the goal shape like this :
Empid | Date | Shift
---------------------
1 |1/01/2019 | A
1 |2/01/2019 | B
1 |3/01/2019 | A
2 |1/01/2019 | B
2 |2/01/2019 | A
2 |3/01/2019 | B
3 |1/01/2019 | B
3 |2/01/2019 | C
3 |3/01/2019 | C
4 |1/01/2019 | A
4 |2/01/2019 | A
4 |3/01/2019 | A
i used this code and reached to this shape using the code :
Empid | Shift
---------------------
1 |A
1 |B
1 |A
2 |B
2 |A
2 |B
3 |B
3 |C
3 |C
4 |A
4 |A
4 |A
this is the vba code :
Sub TransposeData()
Const FirstDataRow As Long = 2 ' presuming row 1 has headers
Const YearColumn As String = "A" ' change as applicable
Dim Rng As Range
Dim Arr As Variant, Pos As Variant
Dim Rl As Long, Cl As Long
Dim R As Long, C As Long
Dim i As Long
With ActiveSheet
Cl = .UsedRange.Columns.Count - .UsedRange.Column + 1
Rl = .Cells(.Rows.Count, Columns(YearColumn).Column).End(xlUp).Row
Set Rng = Range(.Cells(FirstDataRow, YearColumn), .Cells(Rl, Cl))
End With
Arr = Rng.Value
ReDim Pos(1 To (UBound(Arr) * UBound(Arr, 2)), 1 To 2)
For R = 1 To UBound(Arr)
For C = 2 To UBound(Arr, 2)
i = i + 1
Pos(i, 1) = Arr(R, 1)
Pos(i, 2) = Arr(R, C)
Next C
Next R
R = Rl + 5 ' write 5 rows below existing data
Set Rng = ActiveSheet.Cells(R, YearColumn).Resize(i, 2)
Rng.Value = Pos
End Sub
Use Power Query (aka Get & Transform in Excel 2016+).
Select the first column and UNpivot the other columns.
Rename the resultant Date column (which will be named Attributes by the GUI), and the Shift column (which will be named Value by the GUI).
If you want to do this in VBA, record a macro while running PQ
With a single cell selected in your table, select Get & Transform from Table/Range
Power Query will open. Ensure you have selected the first column. Then, from Transform, select the dropdown next to the Unpivot button. From that dropdown, select unpivot other columns.
After selecting that, you will see that you need to rename columns 2 and 3
After that, select one of the Close options from the File menu, and load the results to either the same sheet or another sheet.
Now you can rerun the query if your data changes.
And, as I wrote above, if you need to do this using VBA, just record a macro while you go through the steps.
I also suggest you search SO for unpivot and you'll get a lot of information.
Array Approach
Option Explicit
Public Sub Rearrange()
Dim t#: t = timer ' stop watch
Dim ws As Worksheet ' worksheet object
Set ws = ThisWorkbook.Worksheets("Sheet3") ' << change to sheet name
Const STARTCOL = "A" ' << change to your needs
' [1] get last row in column A
Dim r&, c& ' used rows/cols (assuming no blanks)
r = ws.Range(STARTCOL & ws.Rows.count).End(xlUp).Row
c = ws.Columns(STARTCOL).End(xlToRight).Column - ws.Columns(STARTCOL).Column
' [2] get values to 1-based 2-dim variant arrays
Dim tmp, tgt
tmp = ws.Range(ws.Cells(1, STARTCOL), ws.Cells(r, c + 1)).Value2
ReDim tgt(1 To c * (UBound(tmp) - 1) + 1, 1 To c) ' resize target array
' [3] rearrange data in target array
Dim i&, ii&, j&
For i = 2 To UBound(tmp)
For j = 2 To UBound(tmp, 2) ' get row data
ii = (i - 1) * c + j - c ' calculate new row index
tgt(ii, 1) = tmp(i, 1) ' get ID
tgt(ii, 2) = tmp(1, j) ' get date
tgt(ii, 3) = tmp(i, j) ' get inditgtidual column data
Next j
Next i
tgt(1, 1) = "EmpId": tgt(1, 2) = "Date": tgt(1, 3) = "Shift" ' get captions
' [4] write target array back wherever you want it to ' << redefine OFFSET
ws.Range("A1").Offset(0, c + 2).Resize(UBound(tgt, 1), UBound(tgt, 2)) = tgt
MsgBox "Time needed: " & Format(timer - t, "0.00") & " seconds."
End Sub
Note
You should format the target range with your preferred date formatting, e.g. "dd/mm/yyyy;#" .
I'm new to PowerPivot/DAX and I'm having some trouble with a specific issue I'm trying to resolve. I have a series of products across multiple stores and need to ship a certain amount from as few stores as possible.
A table Products contains a list of products and the needed amounts:
Product | Need
0000001 | 7
0000002 | 8
Another table Stores contains the units available by store and I need to calculate how many to send from each store:
Product | Store | Units | Send
0000001 | 00001 | 5 | 5
0000001 | 00002 | 2 | 2
0000001 | 00003 | 1 | 0
0000002 | 00001 | 0 | 0
0000002 | 00002 | 3 | 1
0000002 | 00003 | 3 | 3
0000002 | 00004 | 4 | 4
0000002 | 00005 | 2 | 0
I have thought of adding a couple of columns for the calculation:
Product | Store | Units | Rank | CSum | Send
0000001 | 00001 | 5 | 1 | 5 | 5
0000001 | 00002 | 2 | 2 | 7 | 2
0000001 | 00003 | 1 | 3 | 8 | 0
0000002 | 00001 | 0 | 5 | 12 | 0
0000002 | 00002 | 3 | 3 | 10 | 1
0000002 | 00003 | 3 | 2 | 7 | 3
0000002 | 00004 | 4 | 1 | 4 | 4
0000002 | 00005 | 2 | 4 | 12 | 0
First, I rank the stores within each product by units available, solving ties at random:
Rank := IF(Units>0,RANKX(ALL(Stores,Stores[Product]),Stores[Units]+RAND())
Then, I calculate the cumulative sum:
CSum := CALCULATE(SUM(Stores[Units]),
FILTER(ALL(Stores,Stores[Product]),Stores[Rank]<=MAX(Stores[Rank])))
Finally, I calculate the amount to send out:
Send := IF(Stores[CSum]>RELATED(Products[Need])+Stores[Units],
IF(Stores[CSum]<RELATED(Products[Need]),
Stores[Units],Stores[Units]-(Stores[CSum]-RELATED(Products[Need]))),0)
Needless to say, I'm getting #ERROR. I think the thought process works, but the formulas are wrong. Also, my Stores table has ~2M records with ~20k products, will I have any problem running this?
I thought of another solution -- Using VBA-code. First I want to give the whole code and then describe some problems:
Const maxStores = 16
Public i As Long
Public j As Integer
Public n As Integer
Public m As Long
Public rangeNeeds As Range
Public rangeHave As Range
Public rangeCost As Range
Sub transportation()
Dim Time1, Time2
Dim Txt As String
Txt = "Enter range with "
Set rangeNeeds = Application.InputBox(prompt:=Txt & "Needs", Type:=8)
Set rangeHave = Application.InputBox(prompt:=Txt & "Inventory", Type:=8)
Set rangeCost = Application.InputBox(prompt:=Txt & "Costs", Type:=8)
' find number of Stores
n = rangeCost.Rows.Count
If n <= maxStores Then
' Algorithm #1
'
'
' Step 1
' ------------------------------------------------------------------------
' make array of binary numbers & sort it
Time1 = Timer
' make array of indexes
Dim ArrIndex() As Long
ReDim ArrIndex(1 To n)
For j = 1 To n
ArrIndex(j) = rangeCost(j, 2)
Next j
' make Indexes
minCost = Application.WorksheetFunction.min(ArrIndex)
For j = 1 To n
If minCost = 0 Then
Debug.Print "Can't count Cost = 0"
Exit Sub
End If
ArrIndex(j) = ArrIndex(j) / minCost
Next j
' make array with indexes
' each index represents
' cost of transportanion
Dim Index As Long
Dim ll As Integer
Dim k, Temp
k = 2 ^ n - 1
ll = Len(k) + 1
Dim Arr()
ReDim Arr(1 To k)
For i = 1 To k
' count total index
For j = 1 To n
Index = Index + CInt(Mid(Dec2Bin(i, n), j, 1)) * ArrIndex(j)
Next j
Temp = Index * 10 ^ ll + i
Arr(i) = Temp
Index = 0
Next i
' sort Array
Call Countingsort(Arr)
' end of Step1
' ========================================================================
'
'
' Step2
' ------------------------------------------------------------------------
' Go throug each value and find the answer
Dim ProdNo As Long ' number of products in order
ProdNo = rangeNeeds.Rows.Count
Dim ArrHave() As Long
ReDim ArrHave(1 To ProdNo)
Dim rangeHaveProd As Range
Dim rangeHaveStor As Range
Dim rangeHaveQuan As Range
Set rangeHaveProd = rangeHave.Columns(1)
Set rangeHaveStor = rangeHave.Columns(2)
Set rangeHaveQuan = rangeHave.Columns(3)
For i = 1 To k ' All Binary Numbers
Temp = CInt(Right(Arr(i), ll - 1))
Temp = Dec2Bin(Temp, n)
' try fulfill the order
For j = 1 To n ' All Stores, n -- index of Store
Index = 0
Index = CInt(Mid(Temp, j, 1))
If Index = 1 Then 'If Store is On
For m = 1 To ProdNo ' All Products, m -- index of Product
ArrHave(m) = ArrHave(m) + _
WorksheetFunction.SumIfs( _
rangeHaveQuan, _
rangeHaveProd, rangeNeeds(m, 1), _
rangeHaveStor, rangeCost(j, 1))
Next m
End If
Next j
' Check if Needs meets
Dim CheckNeeds As Boolean
For m = 1 To ProdNo
If ArrHave(m) < rangeNeeds(m, 2) Then
CheckNeeds = False
Exit For
Else
CheckNeeds = True
End If
Next m
If CheckNeeds Then
Debug.Print "Answer is " & Temp
Exit For
Else
ReDim ArrHave(1 To ProdNo)
End If
Next i
' end of Step2
' ========================================================================
'
'
' Step3
' ------------------------------------------------------------------------
' make report
Sheets.Add
Dim Ws As Worksheet
Set Ws = ActiveSheet
With Range("A1")
.Value = "Report"
.Font.Size = 22
.Font.Bold = True
End With
Rows("4:4").Font.Bold = True
With Ws
' Stores table
.Range("G4") = "Store"
.Range("H4") = "Cost"
.Range("I4") = "On"
rangeCost.Copy
.Range("G5").PasteSpecial xlPasteValues
For i = 1 To n
.Range("I" & 4 + i) = Mid(Temp, i, 1)
Next i
' Needs table
.Range("K4") = "Product"
.Range("L4") = "Need"
rangeNeeds.Copy
.Range("K5").PasteSpecial xlPasteValues
' Have table
.Range("A4") = "Product"
.Range("B4") = "Store"
.Range("C4") = "Units"
.Range("D4") = "On"
.Range("E4") = "Send"
rangeHave.Copy
.Range("A5").PasteSpecial xlPasteValues
.Range("D5:D" & 4 + rangeHave.Rows.Count).FormulaR1C1 = _
"=VLOOKUP(RC[-2],C[3]:C[5],3,0)"
Dim QForm As String
QForm = "=IF(RC[-1]=0,0,IF(SUMIFS(C[7],C[6],"
QForm = QForm & "RC[-4])-SUMIFS(R4C5:R[-1]C,R4C1:R[-1]C[-4],"
QForm = QForm & "RC[-4])-RC[-2]>0,RC[-2],IF(SUMIFS(C[7],C[6],RC[-4])"
QForm = QForm & "-SUMIFS(R4C5:R[-1]C,R4C1:R[-1]C[-4],RC[-4])-RC[-2]<0,"
QForm = QForm & "SUMIFS(C[7],C[6],RC[-4])-SUMIFS(R4C5:R[-1]C,"
QForm = QForm & "R4C1:R[-1]C[-4],RC[-4]),RC[-2])))"
.Range("E5:E" & 4 + rangeHave.Rows.Count).FormulaR1C1 = QForm
Range("A2").FormulaR1C1 = "=""Total Cost = ""&INT(SUMIFS(C[7],C[8],1))"
Range("A2").Font.Italic = True
.Calculate
' convert formulas into values
.Range("D5:E" & 4 + rangeHave.Rows.Count) = .Range("D5:E" & 4 + rangeHave.Rows.Count).Value
End With
' end of Step3
' ========================================================================
'
Time2 = Timer
Debug.Print Format(Time2 - Time1, "00.00") & " sec."
Else
MsgBox "Number of stores exceeds Maximum. Need another Algorithm"
End If
End Sub
'Decimal To Binary
' =================
' Source: http://groups.google.ca/group/comp.lang.visual.basic/browse_thread/thread/28affecddaca98b4/979c5e918fad7e63
' Author: Randy Birch (MVP Visual Basic)
' NOTE: You can limit the size of the returned
' answer by specifying the number of bits
Function Dec2Bin(ByVal DecimalIn As Variant, _
Optional NumberOfBits As Variant) As String
Dec2Bin = ""
DecimalIn = Int(CDec(DecimalIn))
Do While DecimalIn <> 0
Dec2Bin = Format$(DecimalIn - 2 * Int(DecimalIn / 2)) & Dec2Bin
DecimalIn = Int(DecimalIn / 2)
Loop
If Not IsMissing(NumberOfBits) Then
If Len(Dec2Bin) > NumberOfBits Then
Dec2Bin = "Error - Number exceeds specified bit size"
Else
Dec2Bin = Right$(String$(NumberOfBits, _
"0") & Dec2Bin, NumberOfBits)
End If
End If
End Function
Sub Countingsort(list)
Dim counts()
Dim i
Dim j
Dim next_index
Dim min, max
Dim min_value As Variant, max_value As Variant
' Allocate the counts array. VBA automatically
' initialises all entries to 0.
min_value = Minimum(list)
max_value = Maximum(list)
min = LBound(list)
max = UBound(list)
ReDim counts(min_value To max_value)
' Count the values.
For i = min To max
counts(list(i)) = counts(list(i)) + 1
Next i
' Write the items back into the list array.
next_index = min
For i = min_value To max_value
For j = 1 To counts(i)
list(next_index) = i
next_index = next_index + 1
Next j
Next i
End Sub
Function Minimum(list)
Dim i As Long
Minimum = list(LBound(list))
For i = LBound(list) To UBound(list)
If list(i) < Minimum Then Minimum = list(i)
Next i
End Function
Function Maximum(list)
Dim i As Long
Maximum = list(LBound(list))
For i = LBound(list) To UBound(list)
If list(i) > Maximum Then Maximum = list(i)
Next i
End Function
First of all want to tell that task is very familiar to Transportation Problem. So I think of possible maths formulas which can find minimal Costs for transportation.
Problem #1. Big data
This solution goes straight through all combinations. It uses binary numbers to decide which store to select. For example, 01101 means try stores 2,3 and 5. This causes much trouble for computer to count each possibility. So I limited number of stores to 16.
Also I tried this code on 1000 products, not 20k. My computer can't solve this with 20k of products. So someone could make my code work faster.
=>
Problem #2. Costs
The third table is costs of transportation from each store. I added it to model:
| Store | Cost |
| 00001 | 5 |
| 00002 | 2 |
| 00003 | 1 |
| 00004 | 1 |
| 00005 | 10 |
So task is to find minimal transportation cost.
=>
Excel version
I used formula SUMIFS in my code. It will not work in Excel 2003.
=>
Conclusion
I believe this gives you some ideas and help someone else to develop the Code.
I have an Excel sheet that looks like this:
3 | latitude | 46.142737
3 | longitude| -57.608968
8 | latitude | 43.142737
8 | longitude| -52.608968
15 | latitude | 41.142737
15 | longitude| -59.608968
I need the end result to look like this:
3 | 46.142737, -57.608968
8 | 43.142737, -52.608968
15 | 41.142737, -59.608968
It can be concatenated based on every other row, or based on the integer value in the first column.
VBA suggestions? Thank you.
Edit: There is no actual "|" in my Excel sheet. The "|" is meant to be a visual cue representing a new column.
You could read the data into an array and then write that to a range
Original Data:
Result:
Code:
Sub Example()
Dim i As Long
Dim x As Long
Dim arry As Variant
ReDim arry(1 To 2, 1 To 1) As Variant
For i = 1 To ActiveSheet.UsedRange.Rows.Count
If Cells(i, 1).Row Mod 2 = 1 Then
x = x + 1
ReDim Preserve arry(1 To 2, 1 To x) As Variant
arry(1, x) = Cells(i, 1).Value
arry(2, x) = Cells(i, 3).Value & ", " & Cells(i + 1, 3).Value
End If
Next
arry = WorksheetFunction.Transpose(arry)
Sheets("Sheet2").Select
Range(Cells(1, 1), Cells(UBound(arry), UBound(arry, 2))).Value = arry
End Sub
I've got data that looks like this:
BOB | 4
BOB | 3
BOB | 7
MARY | 1
JOE | 2
JOE | 1
MIKE | 6
I want to end up with data that looks like this:
BOB | 4 | 3 | 7
MARY | 1 | |
JOE | 2 | 1 |
MIKE | 6 | |
The problem is, how do I account for the variable number of times a name shows up?
I came up with the following code. It feels like it could be cleaner.
This will work for any selected block of data on your sheet (assuming it is pre-sorted). It outputs on the same sheet in the same area.
Sub WrapDuplicates()
Dim data(), i As Long, startCell As Range, rwCnt As Long, col As Long
data = Selection //pull selected data into an array
Set startCell = Selection.Cells(1, 1) //Get reference to write results to
Selection.ClearContents //remove original data
startCell = data(1, 1) //Output first name
startCell.Offset(0, 1) = data(1, 2) //Output first value
rwCnt = 0
col = 2
For i = 2 To UBound(data) //Loop through array and check if name is same or not and output accordingly
If data(i, 1) = data(i - 1, 1) Then
startCell.Offset(rwCnt, col) = data(i, 2)
col = col + 1
Else
rwCnt = rwCnt + 1
col = 2
startCell.Offset(rwCnt, 0) = data(i, 1)
startCell.Offset(rwCnt, 1) = data(i, 2)
End If
Next i
End Sub
I'm assuming you want to do this in code based on the excel-vba tag in your post.
I'm also assuming the data is sorted by name, or you are OK with sorting it by name before the code executes.
Source is in sheet 1, target is in sheet 2. Code is in Excel VBA. I tested with your sample data, dropping this subroutine in the ThisWorkbook section of the Excel codebehind and pressing play.
The target header gets rewritten every time, which isn't ideal from a performance perspective, but I don't think is a problem otherwise. You could wrap it in an if statement that checks the target column index = 2 if it becomes a problem.
Sub ColumnsToRows()
Dim rowHeading
Dim previousRowHeading
Dim sourceRowIndex
Dim targetRowIndex
Dim targetColumnIndex
sourceRowIndex = 1
targetRowIndex = 1
targetColumnIndex = 2
rowHeading = Sheet1.Cells(sourceRowIndex, 1)
previousRowHeading = rowHeading
While Not rowHeading = ""
If Not previousRowHeading = rowHeading Then
targetRowIndex = targetRowIndex + 1
targetColumnIndex = 2
End If
Sheet2.Cells(targetRowIndex, 1) = rowHeading
Sheet2.Cells(targetRowIndex, targetColumnIndex) = Sheet1.Cells(sourceRowIndex, 2)
previousRowHeading = rowHeading
sourceRowIndex = sourceRowIndex + 1
targetColumnIndex = targetColumnIndex + 1
rowHeading = Sheet1.Cells(sourceRowIndex, 1)
Wend
End Sub
I'm a developer, not an Excel guru. There may be some Excel function, pivot table, or some other Excel magic that does this for you automatically.