data range transformation - vba

I have a list of objects that is being returned from service call.
Object has below attributes:
DateVal1
DateVal2
Value
How do I assign object to range as 2D matrix range:
Date1 as column, Date2 as header row
If there is object with row,column combination print value else print NA.
Data can be large (Max 30x30) so trying to avoid lookup in list every time.
Date 1/31/2015 2/28/2015 3/31/2015
1/1/2015 1 NA NA
1/2/2015 NA 2 NA
1/3/2015 NA NA 3
What if this is slight different?
Object has below attributes:
DateVal1
DateVal2
Value1
Value2
And desired o/p is this:
Date1 Date2 110 20 30
1/1/2015 1/10/2015 1 NA NA
1/2/2015 1/20/2015 NA 2 NA
1/3/2015 1/31/2015 NA NA 3

Untested, but something like this:
Sub Test()
Dim points, i As Long, r As Long, c As Long
Dim dictRows, dictCols, grid(0, 0)
'dictionary to map "key" values to row numbers
Set dictRows = CreateObject("scripting.dictionary")
'dictionary to map "key" values to column numbers
Set dictCols = CreateObject("scripting.dictionary")
points = getPoints()
r = 0
c = 0
'[sort points by date1 here]
'map date1 to "row"
For i = LBound(points) To UBound(points)
If Not dictRows.exists(points(i).date1) Then
r = r + 1
dictRows.Add points(i).date1, r
End If
Next i
'[sort points by date2 here]
'map date2 to "column"
For i = LBound(points) To UBound(points)
If Not dictCols.exists(points(i).date2) Then
c = c + 1
dictCols.Add points(i).date2, c
End If
Next i
ReDim grid(1 To r, 1 To c)
For i = LBound(points) To UBound(points)
grid(dictRows(points(i).date1), dictCols(points(i).date2)) = points(i).Value
Next i
'populate on worksheet
With ActiveSheet
.Range("A2").Resize(r, 1).Value = Application.Transpose(dictRows.keys)
.Range("B2").Resize(r, c).Value = grid
.Range("B1").Resize(1, c).Value = dictCols.keys
End With
End Sub

Related

Excel VBA how to set number sequence to start at middle of the row?

I previously have a Excel sheet with VBA coding that fills column, row 1 to 10 with the number 1, row 11 to 20 with number 2 and so on. The code I've used is as follows:
Sub fill()
Dim ID
ID = 1
For c = 1 To 34
ActiveWorkbook.Sheets("Sheet1").Cells(c, 1) = ID
ActiveWorkbook.Sheets("Sheet1").Cells(c + 1, 1) = ID
c = c + 1
If (c Mod 10) = 0 Then
ID = ID + 1
End If
Next c
End Sub
Now I want to change it so that the code starts at row 3 onwards. Meaning row 3 to 12 = 1, row 13 to 22 = 2 and so on. So I changed the 'For' statement to:
For c = 3 To 34
But what happens is that the number 1 appears from row 3 to row 10, and then continues with number 2 in row 11 to 20. Not what I was expecting.
Therefore, what would be the best method of changing the code?
If you want exactly the same output but two rows lower, you can use:
Sub fill()
Dim ID
ID = 1
For c = 1 To 34
ActiveWorkbook.Sheets("Sheet1").Cells(c + 2, 1) = ID
ActiveWorkbook.Sheets("Sheet1").Cells(c + 3, 1) = ID
c = c + 1
If (c Mod 10) = 0 Then
ID = ID + 1
End If
Next c
End Sub
If you still only want to go to row 34 but start in row 3, change the 34 to 32 in the above code.
You can also do it without looping and this is easier to adjust the parameters:
Sub fill()
Const NUMBER_OF_ROWS As Long = 34
Const START_ROW As Long = 3
Const ID As Long = 1
Const NUMBER_IN_GROUP As Long = 10
With ActiveWorkbook.Sheets("Sheet1").Cells(START_ROW, 1).Resize(NUMBER_OF_ROWS)
.Value = .Parent.Evaluate("INDEX(INT((ROW(" & .Address & ")-" & START_ROW & ")/" & _
NUMBER_IN_GROUP & ")+" & ID & ",)")
End With
End Sub
When i understand you write, this should work:
You can use the loop how you did at the beginning. and just add plus 2 to c in the ActiveWorkbook.Sheets("Tabelle1").Cells(c + 2, 1) = ID
Sub fill()
Dim ID
ID = 1
For c = 1 To 34
ActiveWorkbook.Sheets("Tabelle1").Cells(c + 2, 1) = ID
ActiveWorkbook.Sheets("Tabelle1").Cells(c + 3, 1) = ID
c= c+1
If (c Mod 10) = 0 Then
ID = ID + 1
End If
Next c
End Sub
something like that should be the simplest way:
Sub fill()
Dim i As Integer
Dim j As Integer
For i = 1 To 4
For j = 1 To 10
ActiveWorkbook.Sheets("Sheet1").Cells(j + (i - 1) * 10 + 2, 1) = i
Next j
Next i
End Sub
EDIT:
No, the simplest way would be type formula into A3:
=ROUNDDOWN(((ROW()-3))/10,0)+1
end drag it donw.

sorting data by date with excel

I have raw data I'm trying to sort out by date, the data is in this form:
month:april-2014
offer | value
ofr x | 2132
ofr y | 135
.
.
.
month:mai-2014
offer | value
ofr x | 5115
ofr z | 513
ofr y | 651
and it goes on, there are offers that apear every month and others that dissapear.
I wanted it to look like this :
offer | april-2014 |mai 14 | june ....
ofr x 123 5 6
ofr y 5 1 6
ofr z
ofr a
.
.
any help would be appreciated, thank you
Try to restructure the data like this and use pivot tables?
Date | offer | value
may-2014 |ofr x | 5115
may-2014 |ofr z | 513
may-2014 |ofr y | 651
This first chunk of code is going through and rearranging things for you. The other important thing it does is only sends one column from your selected range to the function. Some important things to remember are you may need to write the search criteria if you key word for "month" is not in the same spot in the text, the word offer is not by itself with no spaces in the following row. Another point of note, is this is treating everything as is. That means if the source cell was text, then the destination cell will be text. To convert from date as text to date as Excel serial that is a separate issue and there are plenty of ways to achieve that as well.
Option Explicit
Sub SortOffer(OfferList As Range)
Dim CounterX As Long, CounterY As Long, jCounter As Long, icounter As Long, MonthCount As Long, UniqueOffers As Long
Dim inlist As Boolean
Dim unsorted() As Variant
Dim sorted() As Variant
MonthCount = WorksheetFunction.CountIf(OfferList, "month*")
UniqueOffers = CountUnique(OfferList.Columns(1).Cells) - MonthCount - 1
ReDim sorted(1 To UniqueOffers + 1, 1 To MonthCount + 1) As Variant
unsorted = OfferList
CounterX = 1
jCounter = 1
sorted(1, 1) = "offer"
For CounterY = LBound(unsorted, 1) To UBound(unsorted, 1)
If Left(unsorted(CounterY, 1), 5) = "month" Then
CounterX = CounterX + 1
sorted(1, CounterX) = Right(unsorted(CounterY, 1), Len(unsorted(CounterY, 1)) - 6)
Else
inlist = False
For icounter = 2 To jCounter
If unsorted(CounterY, 1) = sorted(icounter, 1) Then
sorted(icounter, CounterX) = unsorted(CounterY, 2)
inlist = True
End If
Next icounter
If Not inlist And unsorted(CounterY, 1) <> "offer" And unsorted(CounterY, 1) <> "" Then
jCounter = jCounter + 1
sorted(jCounter, 1) = unsorted(CounterY, 1)
sorted(jCounter, CounterX) = unsorted(CounterY, 2)
End If
End If
Next CounterY
Range("F1").Resize(UBound(sorted, 1), UBound(sorted, 2)).Value = sorted
End Sub
This next function counts the number of unique entries in a range and does not count spaces. I stumbled across this code on this web page. If you subtract the number of months from this count, you will know how many offers are in your table. This is important because it will tell you how to size your array(alt link) that you will later write back as your results
Function CountUnique(ByVal MyRange As Range) As Integer
Dim Cell As Range
Dim J As Integer
Dim iNumCells As Integer
Dim iUVals As Integer
Dim sUCells() As String
iNumCells = MyRange.Count
ReDim sUCells(iNumCells) As String
iUVals = 0
For Each Cell In MyRange
If Cell.Text > "" Then
For J = 1 To iUVals
If sUCells(J) = Cell.Text Then
Exit For
End If
Next J
If J > iUVals Then
iUVals = iUVals + 1
sUCells(iUVals) = Cell.Text
End If
End If
Next Cell
CountUnique = iUVals
End Function
Now just in case the links don't cover it, this answer which was a learning lesson for me was taught in various parts to me by #JNevill, #Ralph, #findwindow, #Gary'sStudent and #ScottCraner. Appologies if I missed someone. I am also sure any of these individuals could do it slicker and take less then 10 hours to write it 8).

Display only certain rows in datatable

So I have a dataset like below
Col1 Col2 Col3 Col4
Apple 1 10 Orange
Apple 2 20 Orange
Apple 3 30 Orange
Apple 1 10 Pear
Apple 2 20 Pear
Apple 3 30 Pear
Orange 1 10 grapes
Orange 2 20 grapes
ORange 1 10 kiwi
Berries 1 10 apple
Berries 1 20 Kiwi
I just need something like
Col1 Col2 Col3 Col4
Apple 1 10 Orange
Apple 2 20 Orange
Apple 3 30 Orange
ORange 1 10 Grapes
Orange 2 20 Grapes
Berries 1 10 Apple
So basically it is the col1 and col4, if col4 changes for col1 as I read through the rows, I should'nt display that
Could someone please help me
After first's suggestion
For i As Integer = 0 To dtResults.Rows.Count - 1
Dim firstItem As String = dtResults.Rows(i)("col1").ToString()
Dim firstToB As String = dtResults.Rows(i)("col4").ToString()
dtResults.DefaultView.RowFilter = "col4= '" + firstToB + "'"
Dim tempTable As DataTable = dtResults.DefaultView.ToTable()
Dim Total As Integer = 0
For Each dr As DataRow In tempTable.Rows
'Dim firstItem As String = dr("col1").ToString()
'If (dr("col1") = firstItem) AndAlso (firstToBin = dr("col4")) Then
If item Is Nothing OrElse item <> dr("col1") Then
If Not item Is Nothing Then
dgv.Rows.Add()
End If
itemnum = dr("col1")
Else
itemnum = ""
desc = ""
size = ""
Total += dr("col3")
End If
item = dr("col1")
dgv.Rows.Add(dr('',dr('',dr('')...)
count += 1
'End If
Next
Next
You will need to filter through your DataTable's rows. You can use the DefaultView in order to do this.
For example:
Dim ds1 As New DataSet1 'Create an instance of your DataSet1
ds1.yourDataTable.DefaultView.RowFilter = "Col4='Orange'"
If your DataSet's DataTables are created in the Visual Studio's designer, you can specify the DataType (string, integer, boolean) for each column. By default the DataType is set to a string.
You can do this in two ways
Filtering and Sorting using DataViews
Filtering and Sorting using DataTables
So to achieve as mentioned in the question here is what I did. I can always make changes in Stored procedure but I should be able to see those rows also in dataset for some other purpose. And so to bind the Grid I took row's item like below loop through
For i As Integer = 0 To dtResults.Rows.Count - 1
Dim firstItem As String = dtResults.Rows(i)("col1").ToString()
If acceptitem Is Nothing OrElse acceptitem <> firstItem Then
Dim firstToB As String = dtResults.Rows(i)("col4").ToString()
dtResults.DefaultView.RowFilter = "col4 = '" + firstToB + "' AND col1 = '" + firstItem + "'"
Dim tempTable As DataTable = dtResults.DefaultView.ToTable()
Dim Total As Integer = 0 ' dtResults.Rows(i)("col3").ToString()
For Each dr As DataRow In tempTable.Rows
acceptitem = dr("col1").ToString()
If item Is Nothing OrElse item <> dr("col1") Then
If Not item Is Nothing Then
dgv.Rows.Add()
End If
itemnum = dr("col1")
Total += Convert.ToInt16(dr("col3").ToString())
Else
itemnum = ""
Total += Convert.ToInt16(dr("col3").ToString())
End If
item = dr("col1")
dgv.Rows.Add(dr('',dr('',dr('').......)
count += 1
Next
End If
Next
Thanks so much for all the other suggestions...

How do I compare values in two columns and then mark true/false in a third?

Can someone please help me create a macro that will search two columns on a worksheet for a list of conditions and mark true/false on the third column. (office 2010)
e.g.
Column A would have the following values: 1111,1,2,3,3,4,...
Column B would have the following values: O,A,Y,A,S,3Y,...
If the following matching conditions are met, the column C would mark as TRUE, otherwise FALSE.
A B
1111 = O
0 = Y
1 = A
2 = S
3 = 3YRY
4 = Q
6 = B
12 = M
13 = V
360 = D
CONDITION RULES:
IF column A = 1111 AND column B = O
OR
IF column A = 0 AND column B = Y
OR
IF column A = 1 AND column B = A
OR
IF column A = 2 AND column B = S
OR
IF column A = 3 AND column B = 3YR
OR
IF column A = 4 AND column B = Q
OR
IF column A = 6 AND column B = B
OR
IF column A = 12 AND column B = M
OR
IF column A = 13 AND column B = V
OR
IF column A = 360 AND column B = D
THEN COLUMN C = "TRUE" ELSE "FALSE"
This should match more closely what you're wanting to accomplish, any questions about what's happening here let me know.
Option Base 1
Sub testCriteria()
'arrays for criteria r, r2. Array for T/F r3
Dim r, r2, r3(10, 1)
'iterators for loop and variable for output column
Dim i As Long, j As Long, c As Long
'column for output of t/f
c = 3
'location of criteria cells h1 through i10
r = [h1:i10]
'location of comparison
r2 = [a1:b10]
'loop through rows of rows to check (r2) and compare with all rows from criteria (r)
For i = LBound(r2) To UBound(r2)
For j = LBound(r) To UBound(r)
If CStr(r(j, 1)) = CStr(r2(i, 1)) _
And CStr(r(j, 2)) = CStr(r2(i, 2)) _
Then r3(i, 1) = "TRUE"
Next j
If Not r3(i, 1) Then r3(i, 1) = "FALSE"
Next i
'reusing iterators for array limits
i = LBound(r3): j = UBound(r3)
'loading t/f array into api
Range(Cells(i, c), Cells(j, c)) = r3
End Sub

WorksheetFunction.Match not working

Here is an excerpt from my data:
------+------+------+------+------+
| A | B | C | D |
------+------+------+------+------+
1 | 10 20 25 30
2 | 152 181 195 210
and my code:
Dim xrng as range, yrng as range, offset as integer
set xrng = Sheets("Sheet1").Range("A1:D1")
set yrng = Sheets("Sheet1").Range("A2:D2")
offset = WorksheetFunction.Match(23, xrng , 1) - 1
Why does running this result in a 1004 error: Unable to get the match property of the worksheetfunction class? How can I fix it?
EDIT: Detailed problem
Okay, I have written a function that does interpolation:
Public Function interpolate(intvalue_X As Double, xrange As range, yrange As range) As Double
....this is just an excerpt:
Dim offst As Integer
offst = WorksheetFunction.Match(intvalue_X, xrange, 1) - 1 'find the offset of the nearest value
---
End Function
With the following data and call, it works fine and returns the correct answer:
(don't mind the variables' who's declarations aren't shown - they have been declared at this point, it's just not copied)
Set intXrng = Sheets("Tables").range("B32:G32")
If beltWidth >= 46 And beltWidth <= 122 And conveyerCenter >= 7.6 And conveyerCenter <= 152.4 Then 'dan kan jy die tabel gebruik
m = interpolate(beltWidth, intXrng, Sheets("Tables").range("B44:G44"))
c = interpolate(beltWidth, intXrng, Sheets("Tables").range("B45:G45"))
powerX = m * conveyerCenter + c
Else
MsgBox "Unable to use the power x-factor table.", vbCritical
End If
Now, when I use the same function, but with this data and call, it gives the error:
Set intXrng = Sheets("Tables").range("F4:I4")
angleSurcharge = 23
capacityTable = interpolate(angleSurcharge, intXrng, Sheets("Tables").range("F7:I7"))
Your values are not stored as strings because they are in a table header. Table headers are always read as strings regardless of their format.
You can convert all values to the Doubles before passing it to Worksheet.Match to fix the bug.
Dim offst As Integer
Dim arry As Variant
ReDim arry(1 To 1, 1 To xrange.Columns.Count)
For i = 1 To xrange.Columns.Count
arry(1, i) = CDbl(xrange.Cells(1, i).Value)
Next
offst = WorksheetFunction.Match(intvalue_X, arry, 1) - 1 'find the offset of the nearest value