Related
Hi All i am trying to generate the word document with two different tables included in it, for this purpose i have two similar methods where i am passing word document reference and data object and table to the similar methods..
Now i am looking to make single method in generic way so that in different places i can use single method by passing parameters to it
Method 1 :
private static List<OpenXmlElement> RenderExhaustEquipmentTableDataAndNotes(MainDocumentPart mainDocumentPart, List<ProjectObject<ExhaustEquipment>> exhaustEquipment,Table table)
{
HtmlConverter noteConverter = new HtmlConverter(mainDocumentPart);
var equipmentExhaustTypes = new Dictionary<string, List<ProjectObject<ExhaustEquipment>>>();
foreach (var item in exhaustEquipment)
{
string exhaustEquipmentName = item.TargetObject.Name;
if (!equipmentExhaustTypes.ContainsKey(exhaustEquipmentName))
{
equipmentExhaustTypes.Add(exhaustEquipmentName, new List<ProjectObject<ExhaustEquipment>>());
}
equipmentExhaustTypes[exhaustEquipmentName].Add(item);
}
List<OpenXmlElement> notes = new List<OpenXmlElement>();
int noteIndex = 1;
foreach (var exhaustEquipmentItem in equipmentExhaustTypes)
{
List<string> noteIndices = new List<string>();
for (int exhaustEquipmentConditionIndex = 0; exhaustEquipmentConditionIndex < exhaustEquipmentItem.Value.Count; exhaustEquipmentConditionIndex++)
{
var condition = exhaustEquipmentItem.Value[exhaustEquipmentConditionIndex];
var row = new TableRow();
Run superscriptRun = new Run(new RunProperties(new VerticalTextAlignment { Val = VerticalPositionValues.Superscript }));
if (exhaustEquipmentConditionIndex == 0)
{
row.Append(RenderOpenXmlElementContentCell(new Paragraph(
new List<Run> {
new Run(new RunProperties(), new Text(exhaustEquipmentItem.Key) { Space = SpaceProcessingModeValues.Preserve }),
superscriptRun
}), 1,
new OpenXmlElement[] {new VerticalMerge { Val = MergedCellValues.Restart },new TableCellMargin {
LeftMargin = new LeftMargin { Width = "120" },
TopMargin = new TopMargin { Width = "80" } }
}));
}
else
{
row.Append(RenderTextContentCell(null, 1, null, null, new OpenXmlElement[] { new VerticalMerge { Val = MergedCellValues.Continue } }));
}
row.Append(RenderTextContentCell(condition.TargetObject.IsConstantVolume ? "Yes" : "No"));
row.Append(RenderTextContentCell($"{condition.TargetObject.MinAirflow:R2}"));
row.Append(RenderTextContentCell($"{condition.TargetObject.MaxAirflow:R2}"));
if (condition.TargetObject.NotesHTML?.Count > 0)
{
foreach (var note in condition.TargetObject.NotesHTML)
{
var compositeElements = noteConverter.Parse(note);
var htmlRuns = compositeElements.First().ChildElements.Where(c => c is Run).Cast<Run>().Select(n => n.CloneNode(true));
notes.Add(new Run(htmlRuns));
noteIndices.Add(noteIndex++.ToString(CultureInfo.InvariantCulture));
}
}
if (exhaustEquipmentConditionIndex == exhaustEquipmentItem.Value.Count - 1 && condition.TargetObject.NotesHTML?.Count > 0)
{
superscriptRun.Append(new Text($"({String.Join(',', noteIndices)})") { Space = SpaceProcessingModeValues.Preserve });
}
table.Append(row);
}
}
List<OpenXmlElement> notesSection = new List<OpenXmlElement>();
List<OpenXmlElement> result = RenderNotesArray(table, notes, notesSection);
return result;
}
and I am calling this method like this in below
var table = new Table(RenderTableProperties());
table.Append(new TableRow(
RenderTableHeaderCell("Name"),
RenderTableHeaderCell("Constant Volume"),
RenderTableHeaderCell("Minimum Airflow", units: "(cfm)"),
RenderTableHeaderCell("Wet Bulb Temperature", units: "(cfm)")
));
body.Append(RenderExhaustEquipmentTableDataAndNotes(mainDocumentPart, designHubProject.ExhaustEquipment, table));
Method 2:
private static List<OpenXmlElement> RenderInfiltrationTableData(MainDocumentPart mainDocumentPart, List<ProjectObject<Infiltration>> infiltration,Table table)
{
HtmlConverter noteConverter = new HtmlConverter(mainDocumentPart);
var nameByInflitrationObject = new Dictionary<string, List<ProjectObject<Infiltration>>>();
foreach (var infiltrationData in infiltration)
{
string infiltrationName = infiltrationData.TargetObject.Name;
if (!nameByInflitrationObject.ContainsKey(infiltrationName))
{
nameByInflitrationObject.Add(infiltrationName, new List<ProjectObject<Infiltration>>());
}
nameByInflitrationObject[infiltrationName].Add(infiltrationData);
}
List<OpenXmlElement> notes = new List<OpenXmlElement>();
int noteIndex = 1;
foreach (var inflitrationDataItem in nameByInflitrationObject)
{
List<string> noteIndices = new List<string>();
for (int inflitrationNameIndex = 0; inflitrationNameIndex < inflitrationDataItem.Value.Count; inflitrationNameIndex++)
{
var dataItem = inflitrationDataItem.Value[inflitrationNameIndex];
var row = new TableRow();
Run superscriptRun = new Run(new RunProperties(new VerticalTextAlignment { Val = VerticalPositionValues.Superscript }));
if (inflitrationNameIndex == 0)
{
row.Append(RenderOpenXmlElementContentCell(new Paragraph(
new List<Run> {
new Run(new RunProperties(), new Text(inflitrationDataItem.Key) { Space = SpaceProcessingModeValues.Preserve }),superscriptRun
}), 1,
new OpenXmlElement[] {new VerticalMerge { Val = MergedCellValues.Restart },new TableCellMargin {
LeftMargin = new LeftMargin { Width = "120" },
TopMargin = new TopMargin { Width = "80" }}
}));
}
else
{
row.Append(RenderTextContentCell(null, 1, null, null, new OpenXmlElement[] { new VerticalMerge { Val = MergedCellValues.Continue } }));
}
row.Append(RenderTextContentCell($"{dataItem.TargetObject.AirflowScalar.ToString("R2", CultureInfo.CurrentCulture)} cfm {EnumUtils.StringValueOfEnum(dataItem.TargetObject.InfiltrationCalculationType).ToLower(CultureInfo.CurrentCulture)}"));
if (dataItem.TargetObject.NotesHTML?.Count > 0)
{
foreach (var note in dataItem.TargetObject.NotesHTML)
{
var compositeElements = noteConverter.Parse(note);
var htmlRuns = compositeElements.First().ChildElements.Where(c => c is Run).Cast<Run>().Select(n => n.CloneNode(true));
notes.Add(new Run(htmlRuns));
noteIndices.Add(noteIndex++.ToString(CultureInfo.InvariantCulture));
}
}
if (inflitrationNameIndex == inflitrationDataItem.Value.Count - 1 && dataItem.TargetObject.NotesHTML?.Count > 0)
{
superscriptRun.Append(new Text($"({String.Join(',', noteIndices)})") { Space = SpaceProcessingModeValues.Preserve });
}
table.Append(row);
}
}
List<OpenXmlElement> notesSection = new List<OpenXmlElement>();
List<OpenXmlElement> result = RenderNotesArray(table, notes, notesSection);
return result;
}
and then i am calling this method here like as below
var table = new Table(RenderTableProperties());
table.Append(new TableRow(
RenderTableHeaderCell("Type"),
RenderTableHeaderCell("Air Flow")
));
body.Append(RenderInfiltrationTableData(mainDocumentPart, designHubProject.Infiltration, table));
i know these are lots of lines but is there any generic way to use single method out of these two similar methods and i am using .net core
Could any one please suggest any idea or suggestion how can i refactor these two methods into single method that would be very grateful.
many thanks in advance
Before we can create a single function that handles both types, achieving the highly laudable goal of removing gratuitous duplication, we should clean the code up to make it easier to see which parts, if any, are different between the two nearly identical methods. And there is a lot to clean up, even if we only had one function.
In short, your functions are too long, having too much much code in one place, and in fact too much code altogether.
In the following, the original code has been broken down into multiple functions with specific purposes and refactored to remove DIY nonsense in favor of the standard library functions and the removal of pointless code.
static IEnumerable<OpenXmlElement> RenderExhaustEquipmentTableDataAndNotes(MainDocumentPart mainDocumentPart, List<ProjectObject<ExhaustEquipment>> exhaustEquipment, Table table)
{
var equipmentByType = exhaustEquipment.ToLookup(item => item.TargetObject.Name);
List<OpenXmlElement> notes = new List<OpenXmlElement>();
foreach (var items in equipmentByType)
{
Run superscriptRun = CreateSuperScriptRun();
foreach (var item in items)
{
var row = new TableRow();
if (item == items.First())
{
row.Append(CreateFirstRowStartingCell(items.Key, superscriptRun));
}
else
{
row.Append(RenderTextContentCell(null, 1, null, null, new[] {
new VerticalMerge { Val = MergedCellValues.Continue }
}));
}
row.Append(RenderTextContentCell(item.TargetObject.IsConstantVolume ? "Yes" : "No"));
row.Append(RenderTextContentCell($"{item.TargetObject.MinAirflow:R2}"));
row.Append(RenderTextContentCell($"{item.TargetObject.MaxAirflow:R2}"));
table.Append(row);
var itemNotes = ParseNotes(mainDocumentPart, item.TargetObject.NotesHTML);
if (item == items.Last() && itemNotes.Any())
{
UpdateSuperScript(superscriptRun, itemNotes);
}
notes.AddRange(itemNotes);
}
}
List<OpenXmlElement> result = RenderNotesArray(table, notes, new List<OpenXmlElement>());
return result;
}
private static Run CreateSuperScriptRun()
{
return new Run(new RunProperties(new VerticalTextAlignment
{
Val = VerticalPositionValues.Superscript
}));
}
private static void UpdateSuperScript(Run superscriptRun, IEnumerable<OpenXmlElement> notes)
{
superscriptRun.Append(new Text($"({string.Join(",", Enumerable.Range(0, notes.Count()))})")
{
Space = SpaceProcessingModeValues.Preserve
});
}
private static IEnumerable<OpenXmlElement> ParseNotes(MainDocumentPart mainDocumentPart, IEnumerable<OpenXmlElement> notes)
{
return notes == null
? Enumerable.Empty<OpenXmlElement>()
: notes.Select(note => new HtmlConverter(mainDocumentPart).Parse(note))
.Select(note => note.First().ChildElements
.OfType<Run>()
.Select(n => n.CloneNode(true))).Select(htmlRuns => new Run(htmlRuns))
.ToList();
}
private OpenXmlElement CreateFirstRowStartingCell(string key, Run superscriptRun)
{
return RenderOpenXmlElementContentCell(
new Paragraph(new List<Run> {
new Run(new RunProperties(), new Text(key) { Space = SpaceProcessingModeValues.Preserve }),
superscriptRun
}),
1,
new OpenXmlElement[] {
new VerticalMerge { Val = MergedCellValues.Restart },
new TableCellMargin { LeftMargin = new LeftMargin { Width = "120" }, TopMargin = new TopMargin { Width = "80" } }
});
}
Now, let's tackle the second function:
static IEnunumerable<OpenXmlElement> RenderInfiltrationTableData(MainDocumentPart mainDocumentPart, IEnunumerable<ProjectObject<Infiltration>> infiltration, Table table)
{
var infiltrationsByType = infiltration.ToLookup(item => item.TargetObject.Name);
List<OpenXmlElement> notes = new List<OpenXmlElement>();
foreach (var inflitrations in infiltrationsByType)
{
Run superscriptRun = CreateSuperScriptRun();
foreach (var item in inflitrations)
{
var row = new TableRow();
if (item == inflitrations.First())
{
row.Append(CreateFirstRowStartingCell(inflitrations.Key, superscriptRun));
}
else
{
row.Append(RenderTextContentCell(null, 1, null, null, new[] {
new VerticalMerge { Val = MergedCellValues.Continue }
}));
}
row.Append(RenderTextContentCell($"{item.TargetObject.AirflowScalar:R2} cfm {item.TargetObject.InfiltrationCalculationType}").ToLower());
table.Append(row);
var itemNotes = ParseNotes(mainDocumentPart, item.TargetObject.NotesHTML);
if (item == inflitrations.Last() && itemNotes.Any())
{
UpdateSuperScript(superscriptRun, itemNotes);
}
notes.AddRange(itemNotes);
}
}
IEnumerable<OpenXmlElement> result = RenderNotesArray(table, notes, new List<OpenXmlElement>());
return result;
}
As we have seen, duplication can be massively reduced simply by extracting code into simple helper functions.
This also makes it far easier to see just where the differences are between the two functions.
It is simply a matter of
row.Append(RenderTextContentCell(item.TargetObject.IsConstantVolume ? "Yes" : "No"));
row.Append(RenderTextContentCell($"{item.TargetObject.MinAirflow:R2}"));
row.Append(RenderTextContentCell($"{item.TargetObject.MaxAirflow:R2}"));
vs.
row.Append(RenderTextContentCell($"{item.TargetObject.AirflowScalar:R2} cfm {item.TargetObject.InfiltrationCalculationType}").ToLower());
To achieve your desired goal of a single function, we can make a generic function, and require that the caller pass in a function that will take care of these differences.
static IEnumerable<OpenXmlElement> RenderTableDataAndNotes<T>(
MainDocumentPart mainDocumentPart,
IEnumerable<ProjectObject<T>> projects,
Table table,
Func<ProjectObject<T>, IEnumerable<OpenXmlElement>> createCells
) where T : ITargetObject
{
var projectsByType = projects.ToLookup(item => item.TargetObject.Name);
List<OpenXmlElement> notes = new List<OpenXmlElement>();
foreach (var items in projectsByType)
{
Run superscriptRun = CreateSuperScriptRun();
foreach (var item in items)
{
var row = new TableRow();
if (item == items.First())
{
row.Append(CreateFirstRowStartingCell(items.Key, superscriptRun));
}
else
{
row.Append(RenderTextContentCell(null, 1, null, null, new[] {
new VerticalMerge { Val = MergedCellValues.Continue }
}));
}
var itemCells = createCells(item);
foreach (var cell in itemCells)
{
row.Append(cell);
}
table.Append(row);
var itemNotes = ParseNotes(mainDocumentPart, item.TargetObject.NotesHTML);
if (item == items.Last() && itemNotes.Any())
{
UpdateSuperScript(superscriptRun, itemNotes);
}
notes.AddRange(itemNotes);
}
}
IEnumerable<OpenXmlElement> result = RenderNotesArray(table, notes, new List<OpenXmlElement>());
return result;
}
Now, when we call it for say some Exhaust Equipment, we do so as follows:
var rendered = RenderTableDataAndNotes(mainDocumentPart, exhaustProjects, table,
exhaust => new[] {
RenderTextContentCell(exhaust.TargetObject.IsConstantVolume ? "Yes" : "No"),
RenderTextContentCell($"{exhaust.TargetObject.MinAirflow:R2}"),
RenderTextContentCell($"{exhaust.TargetObject.MaxAirflow:R2}"),
});
And for infiltration projects, we would do as follows:
var rendered = RenderTableDataAndNotes(
mainDocumentPart,
infiltrationProjects,
table,
infiltration => new[] {
RenderTextContentCell($"{item.TargetObject.AirflowScalar:R2} cfm {item.TargetObject.InfiltrationCalculationType}")
.ToLower()
});
The code could still be substantially improved even now. Currently it requires that the various project types implement a common ITargetObject interface declaring the Name property used to group projects by type. If you refactored your code to reduce nesting by hoisting Name to the ProjectObject<T> type, then we could remove the constraint and the otherwise useless requirement that Infiltration and ExhaustEquipment implement the ITargetObject interface.
Note, if you can't change the types, you can adjust the code in a few ways.
For example, you can remove the type constraint on T and build the lookup outside and pass it to the function:
static IEnumerable<OpenXmlElement> RenderTableDataAndNotes<T>(
MainDocumentPart mainDocumentPart,
ILookup<string, ProjectObject<T>> projectsByType,
Table table,
Func<ProjectObject<T>, IEnumerable<OpenXmlElement>> createCells
)
Then you would call it as
var infiltrationProjectsByType = infiltrationProjects.ToLookup(project => project.Name);
var rendered = RenderTableDataAndNotes(
mainDocumentPart,
infiltrationProjectsByType,
table,
infiltration => new[] {
RenderTextContentCell($"{infiltration.TargetObject.AirflowScalar:R2} cfm {infiltration.TargetObject.InfiltrationCalculationType}").ToLower()
}
);
I am hoping someone can help me with a optimal solution for this. I have a list of time logs. It contains a datatime entry for each clockin per employee per day.
I am trying to come up with a good solution to calculate the time worked per day. The time lapsed between each clock-in and clock-out for one employee.
The final result must give me the following.
Total Time Worked
Total Time Out
The time lapsed between each log / row to extract and show times worked and times out.
For example
ClockIn : 06:00
ClockedOut : 10:00
ClockedIn : 10:15
BreakTime = 00:15
and so on.
I am trying to avoid using to many for loops / foreach loops.
This is a sample piece of code representing what the list of time logs is. The type is. I = for Clock-In and O = Clock-Out. Each Shift has a Block Start Time and Block End Time.
var blockStart = new TimeSpan(0,6,0,0,0);
var blockEnd = new TimeSpan(0,17,0,0);
var listOfTimeLogs = new List<TimeLog>()
{
new TimeLog() {EntryDateTime = new DateTime(2016,05,20,6,0,0),Type = "I"},
new TimeLog() {EntryDateTime = new DateTime(2016,05,20,10,0,0),Type = "O"},
new TimeLog() {EntryDateTime = new DateTime(2016,05,20,10,15,0),Type = "I"},
new TimeLog() {EntryDateTime = new DateTime(2016,05,20,12,0,0),Type = "O"},
new TimeLog() {EntryDateTime = new DateTime(2016,05,20,12,30,0),Type = "I"},
new TimeLog() {EntryDateTime = new DateTime(2016,05,20,15,0,0),Type = "O"},
new TimeLog() {EntryDateTime = new DateTime(2016,05,20,15,15,0),Type = "I"},
new TimeLog() {EntryDateTime = new DateTime(2016,05,20,18,00,0),Type = "O"}
};
Hope this make sense. any help will be appreciated.
Thank you
Computers are made to do loops.
Here is a sample of how I would handle the problem.
Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
namespace TimeClock
{
class Program
{
static void Main()
{
var blockStart = new TimeSpan(0, 6, 0, 0);
var blockEnd = new TimeSpan(0, 17, 0, 0);
var listOfTimeLogs = new List<TimeLog>
{
new TimeLog {EntryDateTime = new DateTime(2016,05,20,6,0,0),EntryType = EntryTypes.In},
new TimeLog {EntryDateTime = new DateTime(2016,05,20,10,0,0),EntryType = EntryTypes.Out},
new TimeLog {EntryDateTime = new DateTime(2016,05,20,10,15,0),EntryType = EntryTypes.In},
new TimeLog {EntryDateTime = new DateTime(2016,05,20,12,0,0),EntryType = EntryTypes.Out},
new TimeLog {EntryDateTime = new DateTime(2016,05,20,12,30,0),EntryType = EntryTypes.In},
new TimeLog {EntryDateTime = new DateTime(2016,05,20,15,0,0),EntryType = EntryTypes.Out},
new TimeLog {EntryDateTime = new DateTime(2016,05,20,15,15,0),EntryType = EntryTypes.In},
new TimeLog {EntryDateTime = new DateTime(2016,05,20,18,00,0),EntryType = EntryTypes.Out}
};
// You are going to have have for / for each unless you use Linq
// fist I would count clock in's versus the out's
var totalIn = listOfTimeLogs.Count(e => e.EntryType == EntryTypes.In);
var totalOut = listOfTimeLogs.Count() - totalIn;
// check if we have in the number of time entries
if (totalIn > totalOut)
{
Console.WriteLine("Employee didn't clock out");
}
// as I was coding this sample program, i thought of another way to store the time
// I would store them in blocks - we have to loop
var timeBlocks = new List<TimeBlock>();
for (var x = 0; x < listOfTimeLogs.Count; x += 2)
{
// create a new WORKING block based on the in/out time entries
timeBlocks.Add(new TimeBlock
{
BlockType = BlockTypes.Working,
In = listOfTimeLogs[x],
Out = listOfTimeLogs[x + 1]
});
// create a BREAK block based on gaps
// check if the next entry in a clock in
var breakOut = x + 2;
if (breakOut < listOfTimeLogs.Count)
{
var breakIn = x + 1;
// create a new BREAK block
timeBlocks.Add(new TimeBlock
{
BlockType = BlockTypes.Break,
In = listOfTimeLogs[breakIn],
Out = listOfTimeLogs[breakOut]
});
}
}
var breakCount = 0;
// here is a loop for displaying detail
foreach (var block in timeBlocks)
{
var lineTitle = block.BlockType.ToString();
// this is me trying to be fancy
if (block.BlockType == BlockTypes.Break)
{
if (block.IsBreak())
{
lineTitle = $"Break #{++breakCount}";
}
else
{
lineTitle = "Lunch";
}
}
Console.WriteLine($" {lineTitle,-10} {block} === Length: {block.Duration.ToString(#"hh\:mm")}");
}
// calculating total time for each block type
var workingTime = timeBlocks.Where(b => b.BlockType == BlockTypes.Working)
.Aggregate(new TimeSpan(0), (p, v) => p.Add(v.Duration));
var breakTime = timeBlocks.Where(b => b.BlockType == BlockTypes.Break)
.Aggregate(new TimeSpan(0), (p, v) => p.Add(v.Duration));
Console.WriteLine($"\nTotal Working Hours: {workingTime.ToString(#"hh\:mm")}");
Console.WriteLine($" Total Break Time: {breakTime.ToString(#"hh\:mm")}");
Console.ReadLine();
}
}
}
TimeBlock.cs
using System;
namespace TimeClock
{
public enum BlockTypes
{
Working,
Break
}
public class TimeBlock
{
public BlockTypes BlockType;
public TimeLog In;
public TimeLog Out;
public TimeSpan Duration
{
get
{
// TODO: Need error checking
return Out.EntryDateTime.Subtract(In.EntryDateTime);
}
}
public override string ToString()
{
return $"In: {In.EntryDateTime:HH:mm} - Out: {Out.EntryDateTime:HH:mm}";
}
}
// a little extension class
public static class Extensions
{
public static bool IsBreak(this TimeBlock block)
{
// if the length of the break period is less than 19 minutes
// we will consider it a break, the person could have clock IN late
return block.Duration.TotalMinutes < 19 ? true : false;
}
}
}
TimeLog.cs
using System;
namespace TimeClock
{
public class TimeLog
{
public DateTime EntryDateTime;
public EntryTypes EntryType;
}
}
EntryTypes.cs
namespace TimeClock
{
public enum EntryTypes
{
In,
Out
}
}
I'm writing a project about game's character data.
And each character in the data document have four types, Lv1 and LvMAX, and HP, STR, VIT, INT, MEN.
I use top one code at the middle part and got NullReferenceException when I use it to get some data like:
int x = CD.Parameters.Basic.Awaked.Force.Lv1.STR;
Force will be null. But when I use buttom one at the middle part, Force won't be null.
What's the difference between that two?
Code below
public class ParamType
{
public ParamLv Mebius, Force, Aegis, Magius;
string cost;
DataRow[] Datas;
List<int> ToMebius = new List<int>(), ToForce = new List<int>(), ToAegis = new List<int>(), ToMagius = new List<int>(); //HP, HP, STR, STR, VIT, VIT, INT, INT, MEN, MEN
public ParamType(SData Data, bool awaked)
{
if (awaked)
{
Data.CharaID = CharaCOM.AwakedID(Data.CharaID);
}
Datas = DataCOM.Search(Data.CharaID, Data.DTs.Source, Data.TitleP.Start[(int)DataTitle.CharacterParams], Const.COL_CHARACTER_ID, Const.COL_CHARACTER_ID);
cost = DataCOM.Search(Data.DTs.Source, Data.CharaID, Const.COL_COST, 0, Data.TitleP.Start[(int)DataTitle.CharacterParams], Const.COL_CHARACTER_ID_WITH_TYPE);
List<int>[] SArray = { ToMebius, ToForce, ToAegis, ToMagius };
for (int i = 0; i < Datas.Length; i++)
{
SArray[i] = new List<int>();
for (int j = Const.COL_PARAM_MIN; j < Const.COL_PARAM_MIN + Const.COL_PARAM_LENGTH; j++)
{
SArray[i].Add(Convert.ToInt32(Datas[i][j]));
}
}
/*
this will send NullReference Exception
ParamLv[] PLArray = new ParamLv[4];
for (int i = 0; i < SArray.Length; i++)
{
PLArray[i] = new ParamLv(Data, SArray[i]);
}
*/
/*
This won't get exception and I can get correct data I want.
Mebius = new ParamLv(Data, SArray[0]);
Force = new ParamLv(Data, SArray[1]);
Aegis = new ParamLv(Data, SArray[2]);
Magius = new ParamLv(Data, SArray[3]);
*/
}
public class ParamLv
{
public Params Lv1, LvMax;
List<int> ToLv1 = new List<int>(), ToLvMAX = new List<int>(); //HP, STR, VIT, INT, MEN
public ParamLv(SData Data, List<int> ParamsL)
{
for (int i = 0; i < ParamsL.Count; i += Const.COL_PARAM_MIN_MAX_GAP)
{
ToLv1.Add(ParamsL[i]);
ToLvMAX.Add(ParamsL[i + 1]);
}
Lv1 = new Params(Data, ToLv1);
LvMax = new Params(Data, ToLvMAX);
}
public class Params
{
//some method and properties to get or set Parameters.
}
}
Please tell me if something still bad, and this is my first time to ask question here, so If I did something wrong, please tell me. Thanks for #MicroVirus , #Moriarty and #mvikhona told my mistake.
Mebius = new ParamLv(Data, SArray[0]);
Force = new ParamLv(Data, SArray[1]);
Aegis = new ParamLv(Data, SArray[2]);
Magius = new ParamLv(Data, SArray[3]);
This works, because you are assigning reference to new ParamLv to your properties.
But in this case:
ParamLv[] PLArray = { Mebius, Force, Aegis, Magius };
for (int i = 0; i < PLArray.Length; i++)
{
PLArray[i] = new ParamLv(Data, SArray[i]);
}
you aren't filling your array with variables/properties themselves, but you are filling it with references your properties hold, in the end your array will hold reference to 4 new ParamLw, but your property Force will stay null.
Edit:
I'll try to explain it a bit different. Let's say you have this code:
ParamLv[] PLArray = { Force };
At this moment value of PLArray[0] is same as value of Force, but PLArray[0] isn't Force.
The moment you do this:
PLArray[0] = new ParamLv(Data, null);
new ParamLv(Data, null) returns reference to new ParamLv and you assign this to your PLArray[0], but like I said before PLArray[0] isn't Force, so Force will stay unchanged.
If that didn't explain it well, try to look at this piece of code, it does what you are trying to do.
int a = 1;
int[] myArr = { a }; // a = 1, myArr[0] = 1
myArr[0] = 2; // a = 1, myArr[0] = 2
object obj = null;
object[] objArr = { obj }; // obj = null, objArr[0] = null
objArr[0] = new object(); // obj = null, objArr[0] = 'reference to new object'
Below is my main list
var serie_line = new { name = series_name, data = new List<object>() };
here I add items in data as follows,
serie_line.data.Add(child_object_name);
serie_line.data.Add(period_final_value );
I then add this var serie_line to another list series as follows,
List<object> series = new List<object>();
series.Add(serie_line);
finally ,I serialize this series into JSON as below,
var obj4 = new { legend = legend, title,chart, series};
JSON_File_Returned = jSearializer.Serialize(obj4);
whereas
System.Web.Script.Serialization.JavaScriptSerializer jSearializer = new System.Web.Script.Serialization.JavaScriptSerializer();
Now Output I am getting is as follows,
{
"legend":{"enabled":"true"},
"title":{"text":"Financial"},
"chart":{"type":"pie"},
"series":[
{"name":"Actual","data":["Market Share",20.00]},
{"name":"Actual","data":["Sales Growth",30.00]},
{"name":"Actual","data":["Operating Profit",40.00]},
{"name":"Actual","data":["Gross Margin %",10.00]}
]
}
But my required output is as follows,
{
"legend":{"enabled":"true"},
"title":{"text":"Financial"},
"chart":{"type":"pie"},
"series":[
{"name":"Actual","data":[["Market Share",20.00],["Sales Growth",30.00],["Operating Profit",40.00],["Gross Margin %",10.00]]}
]
}
So..That I can plot pie chart in highchart using this JSON output...I have tried for everything like using dictionary,making different class and then using it's object and so on...but can't make it out....
Below is my entire code...if in case I am messing with any loop and I don't recognize it but any one might notice it..please check the below code for the same..
var serie_line = new { name = series_name, data = new List<object>() };
for (int k = 0; k <= read_Series_Splitted_Name.Length; k++) //for loop for passing chart type in series
{
for (int i = 0; i < id_series_before_offset.Count; i++) //for loop for counting series ID
{
var xmlAttributeCollection = id_series_before_offset[i].Attributes;
if (xmlAttributeCollection != null)
{
var seriesid = xmlAttributeCollection["id"];
xmlActions_id[i] = seriesid.Value;
resulted_series_id = seriesid.Value;
series_name = Client.GetAttributeAsString(sessionId, resulted_series_id, "name", "");
new_series_name = series_name;
series_Atribute = Client.GetAttributeAsString(sessionId, resulted_series_id, "symbol", "");
if (read_Series_Splitted_Name_store == series_Atribute)
{
serie_line = new { name = series_name, data = new List<object>() };
}
k++;
// Forloop for PeriodId and It's Value
var value = Read_XML_Before_Offset.SelectNodes("//measure.values/series[" + (i + 1) + "]/value");
var xmlActions = new string[value.Count];// for periodname
var xmlActionsone = new string[value.Count]; // for period value
for (int j = 0; j < value.Count; j++)
{
var xmlAttributeCollection_for_period = value[j].Attributes;
if (xmlAttributeCollection_for_period != null)
{
if (i == 0 && a == 0)
{
var periodid = xmlAttributeCollection_for_period["periodid"];
xmlActions[j] = periodid.Value;
period_final_id = periodid.Value;
period_name = Client.GetAttributeAsString(sessionId, periodid.Value, "name", "");
period_Name.Add(period_name);
}
try
{
var action = xmlAttributeCollection_for_period["value"]; // xmlActionsone[j] = action.Value;
period_final_value = float.Parse(action.Value);
// serie_line.data.Add(period_final_value);
serie_line.data.Add(child_object_name);
serie_line.data.Add(period_final_value );
}
catch (Exception ex1)
{
serie_line.data.Add("");
serie_line.data.Add( null );
}
}
}
}
}
}
series.Add(serie_line);
Your C# code should look something like this all stripped down:
var serie_line = new { name = "Actual", data = new List<object>() };
serie_line.data.Add(new List<object>() {"Market Share", 20.0});
serie_line.data.Add(new List<object>() {"Sales Growth", 30.0});
serie_line.data.Add(new List<object>() {"Operting Profit", 40.0});
serie_line.data.Add(new List<object>() {"Gross Margin %", 10.0});
jSearializer.Serialize(serie_line);
Which produces:
{"name":"Actual","data":[["Market Share",20],["Sales Growth",30],["Operting Profit",40],["Gross Margin %",10]]}
I'm not following the bottom part of the code (how you create child_object_name and period_final_value, but I think you want:
serie_line.data.Add(new List<object>() {child_object_name, period_final_value });
Instead of:
serie_line.data.Add(child_object_name);
serie_line.data.Add(period_final_value );
I have a class:
public class ShipmentInformation
{
public string OuterNo { get; set; }
public long Start { get; set; }
public long End { get; set; }
}
I have a List<ShipmentInformation> variable called Results.
I then do:
List<ShipmentInformation> FinalResults = new List<ShipmentInformation>();
var OuterNumbers = Results.GroupBy(x => x.OuterNo);
foreach(var item in OuterNumbers)
{
var orderedData = item.OrderBy(x => x.Start);
ShipmentInformation shipment = new ShipmentInformation();
shipment.OuterNo = item.Key;
shipment.Start = orderedData.First().Start;
shipment.End = orderedData.Last().End;
FinalResults.Add(shipment);
}
The issue I have now is that within each grouped item I have various ShipmentInformation but the Start number may not be sequential by x. x can be 300 or 200 based on a incoming parameter. To illustrate I could have
Start = 1, End = 300
Start = 301, End = 600
Start = 601, End = 900
Start = 1201, End = 1500
Start = 1501, End = 1800
Because I have this jump I cannot use the above loop to create an instance of ShipmentInformation and take the first and last item in orderedData to use their data to populate that instance.
I would like some way of identifying a jump by 300 or 200 and creating an instance of ShipmentInformation to add to FinalResults where the data is sequnetial.
Using the above example I would have 2 instances of ShipmentInformation with a Start of 1 and an End of 900 and another with a Start of 1201 and End of 1800
Try the following:
private static IEnumerable<ShipmentInformation> Compress(IEnumerable<ShipmentInformation> shipments)
{
var orderedData = shipments.OrderBy(s => s.OuterNo).ThenBy(s => s.Start);
using (var enumerator = orderedData.GetEnumerator())
{
ShipmentInformation compressed = null;
while (enumerator.MoveNext())
{
var current = enumerator.Current;
if (compressed == null)
{
compressed = current;
continue;
}
if (compressed.OuterNo != current.OuterNo || compressed.End < current.Start - 1)
{
yield return compressed;
compressed = current;
continue;
}
compressed.End = current.End;
}
if (compressed != null)
{
yield return compressed;
}
}
}
Useable like so:
var finalResults = Results.SelectMany(Compress).ToList();
If you want something that probably has terrible performance and is impossible to understand, but only uses out-of-the box LINQ, I think this might do it.
var orderedData = item.OrderBy(x => x.Start);
orderedData
.SelectMany(x =>
Enumerable
.Range(x.Start, 1 + x.End - x.Start)
.Select(n => new { time = n, info = x))
.Select((x, i) => new { index = i, time = x.time, info = x.info } )
.GroupBy(t => t.time - t.info)
.Select(g => new ShipmentInformation {
OuterNo = g.First().Key,
Start = g.First().Start(),
End = g.Last().End });
My brain hurts.
(Edit for clarity: this just replaces what goes inside your foreach loop. You can make it even more horrible by putting this inside a Select statement to replace the foreach loop, like in rich's answer.)
How about this?
List<ShipmentInfo> si = new List<ShipmentInfo>();
si.Add(new ShipmentInfo(orderedData.First()));
for (int index = 1; index < orderedData.Count(); ++index)
{
if (orderedData.ElementAt(index).Start ==
(si.ElementAt(si.Count() - 1).End + 1))
{
si[si.Count() - 1].End = orderedData.ElementAt(index).End;
}
else
{
si.Add(new ShipmentInfo(orderedData.ElementAt(index)));
}
}
FinalResults.AddRange(si);
Another LINQ solution would be to use the Except extension method.
EDIT: Rewritten in C#, includes composing the missing points back into Ranges:
class Program
{
static void Main(string[] args)
{
Range[] l_ranges = new Range[] {
new Range() { Start = 10, End = 19 },
new Range() { Start = 20, End = 29 },
new Range() { Start = 40, End = 49 },
new Range() { Start = 50, End = 59 }
};
var l_flattenedRanges =
from l_range in l_ranges
from l_point in Enumerable.Range(l_range.Start, 1 + l_range.End - l_range.Start)
select l_point;
var l_min = 0;
var l_max = l_flattenedRanges.Max();
var l_allPoints =
Enumerable.Range(l_min, 1 + l_max - l_min);
var l_missingPoints =
l_allPoints.Except(l_flattenedRanges);
var l_lastRange = new Range() { Start = l_missingPoints.Min(), End = l_missingPoints.Min() };
var l_missingRanges = new List<Range>();
l_missingPoints.ToList<int>().ForEach(delegate(int i)
{
if (i > l_lastRange.End + 1)
{
l_missingRanges.Add(l_lastRange);
l_lastRange = new Range() { Start = i, End = i };
}
else
{
l_lastRange.End = i;
}
});
l_missingRanges.Add(l_lastRange);
foreach (Range l_missingRange in l_missingRanges) {
Console.WriteLine("Start = " + l_missingRange.Start + " End = " + l_missingRange.End);
}
Console.ReadKey(true);
}
}
class Range
{
public int Start { get; set; }
public int End { get; set; }
}