How can I order a PivotTable's ranges by the value in a particular cell (Aspose Cells)? - excel

I need to order the ranges (each range covers four rows) by a value in one of that range's cells. This is how the first bunch of data looks:
I need to order these by the value in the cell at the juxtoposition of "Sum of TotalPrice" and "Grand Total". So in the data shown, the order should be:
ASPARAGUS, STANDARD 11/1#
LETTUCE, SPRING MIX 3#
ASPARAGUS, LARGE 11/1#
LETTUCE, ROMAINE HEARTS 48 CT
CUCUMBERS, ENGLISH (SDLS) 12-14 CT
TOMATOES, 5x6 LOOSE X-LG 25#
CARROTS, BABY PEELED W/TOPS 5# (IMPORT)
TOMATOES, CHERRI 12/1 PT
How can I do that, with Aspose Cells?
In Excel Interop (which is untenable for some of these reports, as it will not even generate the larger ones, even after hours, whereas Aspose Cells can generate them in several minutes), the code is this:
// These two lines don't seem that they would do so, but they do result in the items
// being sorted by (grand) total purchases descending
var fld = ((PivotField)pvt.PivotFields("Description"));
fld.AutoSort(2, "Total Purchases");
As the comments indicate, why this works is a mystery to me, and surely the way to do this in Aspose Cells, and probably more grokkable, but just how is the question.
UPDATE
The following code:
pivotTable.DataFields[0].DisplayName = "Total Packages";
pivotTable.DataFields[1].DisplayName = "Total Purchases";
pivotTable.DataFields[2].DisplayName = "Avg Purchase";
pivotTable.DataFields[3].DisplayName = "% of Total";
pivotTable.RowFields[0].ShowInOutlineForm = true;
pivotTable.RowFields[0].ShowCompact = true;
PivotField field = pivotTable.RowFields[1];
field.IsAutoSort = true;
field.IsAscendSort = false;
field.AutoSortField = 1; // 0 based indexed position in the PivotTable.DataFields
pivotTable.PivotTableStyleType = PivotTableStyleType.PivotTableStyleLight16;
pivotTable.RefreshDataFlag = true;
pivotTable.RefreshData();
pivotTable.CalculateData();
pivotTable.RefreshDataFlag = false;
...but it did not work for me; it failed with, "You can't operate the field" on this line:
field.IsAscendSort = false;

We need your template Excel file (if any), output Excel file (containing Pivot Table) by Aspose.Cells APIs and an Excel file that you may manually create in Ms Excel that should contain the PivotTable with your desired order set for the underlying ranges. We recommend you to follow up your Aspose forum thread.
I am working as Support developer/ Evangelist at Aspose.

Related

C++ Excel Automation : How to use SetFormula function for the cell elements whose row & column were unknown but will be given by the program later on?

I am working on the heritage codes which use C++ Excel Automation to output our analysis data in the excel spreadsheet. From the following article,
https://support.microsoft.com/en-us/topic/how-to-use-mfc-to-automate-excel-and-create-and-format-a-new-workbook-6f2450bc-ba35-a36a-df2f-c9dd53d7aef1
I knew we can use "range.SetFormula() function to calculate the formula results from some specific cells, for example:
range = sheet.GetRange(COleVariant("C2"), COleVariant("C6"));
range.SetFormula(COleVariant("=A2 & \" \" & B2"));
My question here is how can I use SetFormula function to point to some cell elements whose row & column are unknow but will be determined as the program runs. In specifically, I have a number of cell elements populated as my analysis runs. Different analysis will have different number of elements output to the excel spreadsheet. For example, if I have kw data, then the excel output will be populated in kw row 6 column and I also need to output some summary results based on these element underneath these populated elements. Something like this:
int kw = var_length; // the row changes depending on different analysis
DWORD numElements[2];
Range range;
range = sheet.GetRange(COleVariant(_T("A3")),COleVariant(_T("A3")));
numElements[0]= kw; //Number of rows in the range.
numElements[1]= 6; //Number of columns in the range.
saRet.Create(VT_R8, 2, numElements);
for(int iRow = 0;iRow < kw; iRow++)
{
for (iCol = 0; iCol < 6; iCol++)
{
index[0] = iRow;
index[1] = iCol;
saRet.PutElement(index, &somevalue);
}
}
range.SetValue2(COleVariant(saRet));
CString TStr;
TStr.Format(_T("A%d"), kw+2);
range = sheet.GetRange(COleVariant(TStr), COleVariant(TStr))
CString t1, t2;
t1.Format(_T("A%d"), kw/2);
t2.Format(_T("A%d"), kw);
range.SetFormula(COleVariant(L"=SUM(A&t1: A&t2)")); // Calculate the sum of second half of whole elements, Apparently, this didn't work, How can I fix this?
Here I want to sum the second half of whole elements but in the SetFormula function, I didn't know exactly row number for these element, eg, A25 - A50. The row number is dependent on the kw which is given as input from program. Different analysis, kw is different. I attempted to use TStr format to get the row number but it CAN NOT be used inside SetFormula function. Ideally I want to use formula for my summary data output so that if I change my populated the element values, the summary data output can change accordingly. I searched in your MSDN website but couldn't find any solution on how to resolve this.
Can someone help me with the issue?
Thanks in advance.

Excel formula where certain combinations of cells would return a value

I'm creating a stakeholder map and I have two columns with two possible entries for each
Power | Interest | Strategy
Power and Interest can be either High or Low
So I am trying to get a strategy to return a value based on whether Power or Interest is one of those two combinations as an example:
Power = High, Interest = High, Strategy = Manage Closely
Power = Low, Interest = High, Strategy = Keep Informed
Power = Low, Interest = Low, Strategy = Monitor
Power = High, Interest = Low, Strategy = Meet Their Needs
Can someone help me with this formula?
Make a new column where you concatenate Power and interest.
A B C D
1 Power Interest Concat Result
2 High Low HighLow
3 High High HighHigh
Then somewhere down in the excel (or in another tab) make a relationship between inputs and outputs
A B C D
1000 Power Interest Concat Result
1001 High High HighHigh Manage Closely
1002 High Low HighLow Kep Informed
1003 Low High LowHigh Monitor
1004 Low Low LowLow Meet Their Needs
finally use the following formula in the cell D2 and slide it down to all your rows
=VLOOKUP(C2,$C$1001:$D$1004,2)
Where C is the concatenated column, C1001:D1004 is the relationship table location.
You can then hide the column C if you wish.
So, a long way is to use if() with AND() like so:
IF(AND(B1="high",D1="High"),"Manage closely",IF(AND(B1="Low",D1="High"),"Keep informed",IF(AND(B1="Low",D1="Low"),"Monitor",IF(AND(B1="high",D1="Low"),"Meet their needs"))))
But a table with vlookup() is easier to control as per the other answer. However, this does not use a helper column...
I tried this formula and it worked for me:
=IF(AND(A2="High";B2="High");"Manage Closely"; IF(AND(A2="Low";B3="High");"Keep Informed"; IF(AND(A2="Low";B2="Low");"Monitor"; IF(AND(A2="High";B2="Low");"Meet Their Needs";0))))
Drag down/Copy and paste the formula under the Strategy Column and it should turn out like this:
Another solution:
You can use built-in function below after saving your file as an .xlsm file and saving the code in a new module in file:
Function myStrategy(power As String, Interest As String) As String
If LCase(power) = "high" And LCase(Interest) = "high" Then
myStrategy = "Manage Closely"
ElseIf LCase(power) = "low" And LCase(Interest) = "high" Then
myStrategy = "Keep Informed"
ElseIf LCase(power) = "low" And LCase(Interest) = "low" Then
myStrategy = "Monitor"
ElseIf LCase(power) = "high" And LCase(Interest) = "low" Then
myStrategy = "Meet Their Needs"
End If
End Function

How to remove duplicates from individual powerquery columns without removing entire rows

I have a data table that records cost savings data and I have 1 row per project. This has overall project type data such as annual spend, annual savings, etc. but also has the months the savings fall into. To pivot on this data, I converted it to a table with PowerQuery but some columns repeat such as annual spend for each month where there are savings so I might get 10 rows for savings which is correct, but the annual spend is duplicated 10 times. Can I remove duplicates in just those columns retaining the other data.
I have searched and tried various solutions but haven't found one that works. I am not set on data table format, so am open to anything.
Below is a sample of the data
Sample of PowerQuery
As you will see, Baseline Spend, Negotiated Spend, Savings Amount are all shown for each row and I need to use these in a pivot/slicer.
Any help would be appreciated.
Regards,
Keith
I think one solution might be to "only keep the first1 annual spend per project". More abstractly, "only keep the first value in column(s) X per column(s)Y".
Below is some mock/dummy data. I only want to keep the highlighted values in my annual spend column (as the highlighted values are the first "annual spend" figures per "project").
This is the M code I'm using to achieve this. (To try it, open the Query Editor > Advanced Editor (near top right) > copy-paste code below to there > OK).
let
OnlyKeepFirstValueInColumn = (someTable as table, columnsToNullify as list) as table =>
let
firstRow = Table.FirstN(someTable, 1), // This assumes first row contains a non-blank value.
remainingRows = Table.Skip(someTable, 1),
loopAndNullify = List.Accumulate(columnsToNullify, remainingRows, (tableState, currentHeader) => Table.TransformColumns(tableState, {{currentHeader, each null}})),
combined = firstRow & loopAndNullify
in combined,
FirstValueOfColumnsPerGroup = (someTable as table, groupByColumns as list, columnsToNullify as list) =>
let
group = Table.Group(someTable, groupByColumns, {{"toCombine", each OnlyKeepFirstValueInColumn(_, columnsToNullify), type table}}),
combined = Table.Combine(group[toCombine])
in combined,
aggregatedTable = Table.FromColumns({Text.ToList("aaabbbccccdddeeefg"), List.Repeat({1000}, Text.Length("aaabbbccccdddeeefg"))}, type table [project=text, annual spend=number]),
transformed = FirstValueOfColumnsPerGroup(aggregatedTable, {"project"}, {"annual spend"})
in
transformed
The important bit to understand is this line:
transformed = FirstValueOfColumnsPerGroup(aggregatedTable, {"project"}, {"annual spend"})
in which you should replace:
aggregatedTable with whatever variable/expression contains your table
{"project"} with the name of your "project" column (keep the curly braces {} though as they let you pass in several columns if needed)
{"annual spend"} with the names of whichever column(s) you want to keep only the first value in (keep the curly braces {})
This is what I get (which I think is similar to what you want):
1To keep things simple, we'll say "first" here means the value in the first row. It could have meant "first non-null value" or "first value satisfying some particular condition or logic", but your data suggests the simpler definition will work okay.

How to get cell value from another excel sheet based on multiple list conditions

I apologise if this comes across as a newbie query, but I have been stumped by this for some time.
I am building an excel spreadsheet for my staff to be able to compare stock listings easily and automatically based on reference data held in a different sheet within the same workbook.
The difficulty is that I want to pull this reference data based on the values chosen from a total of 6 conditional drop down lists I have created.
Based on what is selected, the values on say Sheet 1 will display the relevant values from Sheet 2 automatically (i.e. if one of the list options is changed, the "results" will update with the associated data automatically).
I have tried using lookup functions, arrays and nested if functions, but I have been unable to get it to work properly - probably due to my own lack of depth of knowledge for this type of coding. As such I am looking for advice about the best way to code this function requirement.
The format of what I am trying to do is this:
Sheet 1 - The 'GUI'
This sheet has the dropdown lists and the "displayed results" section
dd1 - State (6 options)
dd2 - Product Type (4 options)
dd3 - Product Brand (3 options)
dd4 - Product variation 1 (eg model) (say, 2 options)
dd5 - Product variation 2 (eg colour) (5 options)
dd6 - Product variation 3 (eg size) (3 options)
Sheet 2 - Reference data
Table 1 - ACT products and variations
Table 2 - VIC products and variations
Table 3 - NSW products and variations etc and so on...
Based on choices above in Sheet1, I want the product & price to be listed in cells below. Currently I have conditional functions listing the avail options based on the choice made for the prev list working fine.
I need it so that:
IF dd1 = "ACT" AND dd2 = "Mobile" AND dd3 = "Apple" AND dd4 = "iPhone" AND dd5 = "white" AND dd6 = "128Gb"
Then
The results cell, say Sheet1!C10, will reflect the price of the item based on a table of data lists in Sheet2 (say for eg the listed value is in Sheet 2, Cell A5)
But if say the list choices change, eg dd3 was changed to "Samsung", then the lists will auto update because of the conditional formatting already; but I then want the results to be updated accordingly (eg the price listed based on the new choices will then reflect Sheet2!B3).
Because of the many options and variations, there are many tables in Sheet 2 for reference, so the 'IF' statement to cover all these options would be massive and convoluted.
Is there a relatively concise way of writing vb code for this function to suit?
My current code under the worksheet_change sub
If Range("D10").Value = "ACT" And (Range("D11").Value = "Mobile" And (Range("D13").Value = "Samsung" And (Range("D14").Value = "S7") And (Range("D15").Value = "Black") And (Range("D16").Value = "128"))))) Then
Sheets("Sheet2").Range("F20").Value = Sheets("Sheet4").Range("C5").Value2
Sheets("Sheet2").Range("F29").Value = Sheets("Sheet4").Range("C6").Value2
ElseIf Range("D10").Value = "ACT" And (Range("D11").Value = "Mobile" And (Range("D13").Value = "Samsung" And (Range("D14").Value = "S7") And (Range("D15").Value = "Black") And (Range("D16").Value = "256"))))) Then
Sheets("Sheet2").Range("F21").Value = Sheets("Sheet4").Range("C8").Value2
Sheets("Sheet2").Range("F22").Value = Sheets("Sheet4").Range("C9").Value2
Sheets("Sheet2").Range("F29").Value = Sheets("Sheet4").Range("C10").Value2
'and so on. With all the variations, there's prob going to be approx 20+ elseif statements.
End If
Ive only pasted 2x sections for the purpose of this request. I can copy it down or adjust the code accordingly for each option.
Ive not used nor written arrays before (successfully i.e.) so I have no clue/understanding if that is a better way, but i have written vba for basic functions and macros before; but im open to learn.

Lotus Notes: Displaying days of a month, following the weekdays of it

I have a not so nice question. I've been thinking about this for like a month now and read a couple of books but can seem to find an answer or how to execute this. As you all know, I'm making this application that generates date. I have one combobox it has months in it, starting january to december, two column table, first colum displays the day and the second one displays the weekdays, on selecting month combobox, it must display the days in that month on first column and weekdays on 2nd column, by row. example: I choose january, on the first column it will display the days of that month, 1st row is 1, 2nd row is 2, and so on, and same as weekdays. I'm kinda new to LN. Can you give me an idea for it? Hope you can help me out.
This is a solution based on Notes #Formula. Only a few lines of code are necessary to achieve the result.
First we need the form
The formula for Days is
_days :=1:2:3:4:5:6:7:8:9:10:11:12:13:14:15:16:17:18:19:20:21:22:23:24:25:26:27:28:29:30:31;
_numberDays := #Day(#Adjust(#Adjust(#Date(#ToNumber(Year); #ToNumber(Month); 1); 0; 1; 0; 0; 0; 0); 0; 0; -1; 0; 0; 0));
#Subset(_days; _numberDays)
and the formula for Weekday is
_weekdays := #Transform( #ToNumber(Days); "day";
#Weekday(#Date(#ToNumber(Year); #ToNumber(Month); day)));
#Replace(#Text(_weekdays);
"1":"2":"3":"4":"5":"6":"7";
"Sunday":"Monday":"Tuesday":"Wednesday":"Thursday":"Friday":"Saturday")
That's it.
The fields Month and Year have to have the property "Refresh fields on keyword change".
The fields Days and Weekday need "Computed for display", "Allow multiple values" and "New Line" as separate values.
The result will look like this
Truly dynamic tables are difficult. In this case it's definitely possible because you have a defined number of rows, but it's still somewhat messy.
I'm not aware of anything built in that will easily tell you how many days there are in each month. I'd probably just create a config doc with year, month, and numberOfDays fields, and hidden view that you can use for lookups. You're going to need this in many places, and you don't want to do the lookup each time, so do it in a hidden computed field that comes after your dropdown but before your table. (Be sure to set properties so the field is recomputed after the value in the dropdown is changed.) Call the field something like daysInSelectedMonth.
Obviously the first column is easy: just create your table and enter the numbers 1 through 31, and apply a hide-when formula to the cells for rows 29 through 31 so that they only show up if daysInSelectedMonth is the right number of days. You don't need the hide when in the other rows.
For the second column, you will need to use computed for display fields. I would strongly suggest naming them something like weekday_1, weekday_2,... weekday_31 so that you can use #ThisName and some simple string manipulation to extract the number from the field name. That will tell you what row the formula is in, and it is your day number. The benefit of doing it this way is that your formula can be exactly the same in every one of the fields -- just a cut-and-paste into the other fields after you get it right once.
I would suggest starting to work on the formula in the weekday_31 field, and when you get it right (showing the correct weekday in a month that does have 31 days, and blank in a month that does not), then you can copy the formula to the rest of the fields. You will need to use an #If to detect whether the month has the correct number of days -- this is easy, except for leap year. I'm going to leave that part up to you. Just make it return "" if the month does not have the right number of days, and then have the final clause of the #f use #Date to build the value for the date that you are working on and then use the #Weekday function to display the value.
It all depends on a few things:
Web application or Notes client application?
What is the end result of the exercise, i.e. what is the table intended to be used for? Display purposes only?
Making some assumptions (Notes client application, and table used only for display), I see two ways to do this.
An easy way to do this is to create the table with 31 rows and 2 columns.
In the cells you create fields: Day1, Weekday1, Day2, Weekday2, etc.
You also need a year field, unless it is always current year.
Set a hide-when formula on rows 29-31, to hide if the Day field for that row is blank.
On the advanced properties tab for the combobox where you select month, set "Run Exiting/OnChange events after value change".
In the Exiting event for the combobox, write some Lotusscript that populate the fields with days and weekdays, based on selected year and month. Something like this (untested:
Sub Exiting(Source As Field)
Dim session As New NotesSession
Dim ws As New NotesUIWorkspace
Dim uidoc As NotesUIDocument
Dim monthName As String
Dim YYYY As String
Dim firstDate As NotesDateTime
Dim lastDate As NotesDateTime
Dim n As Double
Dim i As Integer
Dim dayname(1 To 7) As String
dayname(1) = "Sunday"
dayname(2) = "Monday"
dayname(3) = "Tuesday"
dayname(4) = "Wednesday"
dayname(5) = "Thursday"
dayname(6) = "Friday"
dayname(7) = "Saturday"
Set uidoc = ws.CurrentDocument
YYYY = uidoc.FieldGetText("Year")
monthName = uidoc.FieldGetText("Month")
Set firstDate = New NotesDateTime("1 " & monthName & ", " & YYYY)
Set lastDate = New NotesDateTime(firstDate.DateOnly)
Call lastDate.AdjustMonth(1)
Call lastDate.AdjustDay(-1)
i = 0
For n = Cdbl(firstDate.LSLocalTime) To Cdbl(lastDate.LSLocalTime)
i = i + 1
Call uidoc.FieldSetText("Day" & i, Cstr(i))
Call uidoc.FieldSetText("Weekday" & i, dayname(Weekday(Cdat(n))))
Next
Call uidoc.Refresh()
End Sub
Another way to create a truly dynamic table, is the method I blogged about here:
http://blog.texasswede.com/dynamic-tables-in-classic-notes/
The benefit is that it is more flexible, and you can create a nicer layout without needing to create a large number of fields.

Resources