Resetting the order in schedules - c#

The goal of this macros (app) is to reset the order of elements inside of a schedule we have opened now. Order starts with a user chosen number. Numbers are stored inside every element's parameter that user can specify. We get IDs of elements by hitting "IDs of Selection".
Resetting the order
public void AutoNumerate()
{
Document doc = this.ActiveUIDocument.Document;
ViewSchedule vs = doc.ActiveView as ViewSchedule;
TableData tData = vs.GetTableData();
TableSectionData tsDada = tData.GetSectionData(SectionType.Body);
int startIndex = 1; //SETTING STARTING NUMBER
using (TransactionGroup tGroup = new TransactionGroup(doc,"Numeration: "+vs.Name))
{
tGroup.Start();
for (int rInd = 0; rInd < tsDada.NumberOfRows; rInd++)
{
SetNum(doc,startIndex++, );
}
tGroup.Assimilate();
}
}
public void SetNum(Document doc, int num, List<Element> Myelements)
{
using (Transaction tr = new Transaction(doc,"Creating elements based on IDs"))
{
tr.Start();
//List<ElementId> eleIds = new List<ElementId>{946164,946385,946484,946631,946708,946759,946816};
int[] eleIds = {946164,946385,946484,946631,946708,946759,946816};
foreach (int id in eleIds)
{
List<Element> MyElements = GetElement();
}
foreach (Element ele in MyElements)
{
//ele.LookupParameter("Comments").Set(num.ToString());
ele.LookupParameter("Comments").Set(num.ToString()).ToList(); //SPECIFYING PARAMETER
}
tr.Commit();
}
}
private List<Element> GetElementsOnRow(Document doc, ViewSchedule vs, int rowNumber)
{
TableData tableData = vs.GetTableData();
TableSectionData tableSectionData = tableData.GetSectionData(SectionType.Body);

To sort a schedule you need to use the ScheduleDefinition.AddSortGroupField method. Create a ScheduleSortGroupField from the ScheduleFieldId of the 'Comments' parameter to input into the method.

Related

How to loop list class

I have below class. And data is serialized using JsonConvert.DeserializeObject. The data is returning from list. But I want to loop using for or for each. Please advice.
Thank you.
var result = client.getInvocieLine
(call Context);
string strResultJson = JsonConvert.SerializeObject(result);
System.IO.File.WriteAllText(#"D:\jsondata\invoicedata.json", strResultJson);
string fileName = #"D:\jsondata\invoicedata.json";
string jsonText = System.IO.File.ReadAllText(fileName)
List<EInvoiceModel.Class1> data = Newtonsoft.Json.JsonConvert.DeserializeObject<List<EInvoiceModel.Class1>>(jsonText);
EInvoiceModel.Invoiceline invoicelined = new EInvoiceModel.Invoiceline();
for(int i=0; i < data.Count; i++)
{
invoicelined.parmItemId = data[i].invoiceLines[i].parmItemId; //Its bring only one record. I need all record from data. Please advice.
}
return Ok(invoicelined);
Please find my EInvoiceModel contains below structure, Which i designed as per my json response. yes I need same index for data and invoiceLines in data[i].invoiceLines[i]. How can I get this to return all values. Index currently pointing only one record and return one record. If I change position its return another record. But I need all please.
public class Class1
{
public Invoiceline[] invoiceLines { get; set; }
public string parmCustName { get; set; }
public DateTime parmInvoiceDate { get; set; }
} public class Invoiceline
{
public string parmItemId { get; set; }
public string parmItemNameDisplay { get; set; }
public float parmQty { get; set; }}
Please find my json structure. I wist to repeat all parameter-ids from json response.
{"invoiceLines":[{"parmCurrencyCode":null,"parmCustExchRate":0.0,"parmInvoiceId":null,"parmItemId":null,"parmItemNameDisplay":null,"parmQty":0.0,"parmSalesLinePercent":0.0,"parmSalesUnit":null,"parmdiscountAmount":0.0,"parmnetTotal":0.0,"parmsalesPrice":0.0,"parmsalesTotal":0.0,"parmtotalItemsLineDisc":0.0,"parmtotalTaxableFees":0.0}],"parmCustName":null,"parmInvoiceDate":"0001-01-01T00:00:00","parmInvoiceId":null,"parmSalesId":null,"parmnetAmount":0.0,"parmtotalAmount":0.0,"parmtotalAmountWithDisc":0.0,"parmtotalItemsDiscountAmount":0.0,"parmtotalSalesAmount":0.0}
Please I want return all parmItemId below way. I don't want damage my json structure.
EInvoiceModel.Class1 ds = new EInvoiceModel.Class1();
List<EInvoiceModel.Class1> data = Newtonsoft.Json.JsonConvert.DeserializeObject<List<EInvoiceModel.Class1>>(jsonText);
// List<EInvoiceModel.Invoiceline> data = Newtonsoft.Json.JsonConvert.DeserializeObject<List<EInvoiceModel.Invoiceline>>(jsonText);
for (int i = 0; i < data.Count; i++)
{
ds.invoiceLines = new List<EInvoiceModel.Invoiceline>
{
new EInvoiceModel.Invoiceline
{
parmItemId = data[i].invoiceLines[i].parmItemId
}
};
}
return Ok(ds);
You have used to Generic list
To get all list
List<EInvoiceModel.Invoiceline> data = Newtonsoft.Json.JsonConvert.DeserializeObject<List<EInvoiceModel.Invoiceline>>(jsonText);
List<EInvoiceModel.Invoiceline> invoicelines = new List<EInvoiceModel.Invoiceline>();
for(int i=0; i < data.Count; i++)
{
invoicelines.Add(new EInvoiceModel.Invoiceline()
{
parmItemId = data[i].parmItemId
});
}
return Ok(invoicelines);
You now have this:
EInvoiceModel.Class1 ds = new EInvoiceModel.Class1();
for (int i = 0; i < data.Count; i++)
{
ds.invoiceLines = new List<EInvoiceModel.Invoiceline>
{
new EInvoiceModel.Invoiceline
{
parmItemId = data[i].invoiceLines[i].parmItemId
}
};
}
This means that on every iteration you overwrite that ds.invoiceLines with a new list, containing a single item. So you end up with only the last one.
A possibly better way would be this:
EInvoiceModel.Class1 ds = new EInvoiceModel.Class1();
ds.invoiceLines = new List<EInvoiceModel.Invoiceline>(); // create a list once
for (int i = 0; i < data.Count; i++)
{
// add one item to that list
ds.invoiceLines.Add(new EInvoiceModel.Invoiceline
{
parmItemId = data[i].invoiceLines[i].parmItemId
}
);
}
This way you assign a new (empty) list once (you can skip this when invoiceLines already contains an empty list, after you created a new Class1).
And then, in the loop, you don't assign a new list, but just add a new item to that existing list. At the end, you get a list with as many items as there are in your input data.
By the way, this assumes that you really need to make a list of data[i].invoiceLines[i].parmItemId. I find it curious that for data[3] you need an invoiceLines[3] and for data[7] an invoiceLines[7]. I would expect one loop through all data and then an inner loop over all its invoiceLines. But you know your data best.
Apparently, you do need to loop over the inner objects. So modify that code like this:
EInvoiceModel.Class1 ds = new EInvoiceModel.Class1();
ds.invoiceLines = new List<EInvoiceModel.Invoiceline>(); // create a list once
// loop over all "data" items
for (int i = 0; i < data.Count; i++)
{
// then loop over the invoiceLines inside the current data item
for (int j = 0; j < data[i].invoiceLines.Count(); j++)
{
// add one item to that list
ds.invoiceLines.Add(new EInvoiceModel.Invoiceline
{
parmItemId = data[i].invoiceLines[j].parmItemId
}
);
}
}
Note that I still use index i in data[i] from the outer loop, but now use index j in invoiceLines[j] from the inner loop.

Add values to a list within a foreach loop

I simply wanted to add float values to list within a foreach loop. The code perfectly reads row.Size as well as row.Price and adds additional RData1 sets to InDat, but uses always the "latest" dataset of RData1.Size and RData1.Price instead of keeping the "previous" loop values. So after closing the loop, I get the same values in each line of the list.
IEnumerable<ModelInput> inputData = mlC.Data.CreateEnumerable<ModelInput>(tView,reuseRowObject: true);
public List<InputData> InDat { get; set; }
InDat = new List<InputData>();
InputData RData1 = new InputData();
int count = 0;
foreach (ModelInput row in inputData)
{
count++;
RData1.Size = Convert.ToString(row.Size);
RData1.Price = Convert.ToString(row.Price);
InDat.Add(RData1);
}
You need to instantiate Rdatat1 inside the loop:
int count = 0;
foreach (ModelInput row in inputData)
{
count++;
InputData RData1 = new InputData();
RData1.Size = Convert.ToString(row.Size);
RData1.Price = Convert.ToString(row.Price);
InDat.Add(RData1);
}

binary search in a sorted list in c#

I am retrieving client id\ drum id from a file and storing them in a list.
then taking the client id and storing it in another list.
I need to display the client id that the user specifies (input_id) on a Datagrid.
I need to get all the occurrences of this specific id using binary search.
the file is already sorted.
I need first to find the occurrences of input_id in id_list.
The question is: how to find all the occurrences of input_id in the sorted list id_list using binary search?
using(StreamReader sr= new StreamReader(path))
{
List<string> id_list = new List<string>();
List<string> all_list= new List<string>();
List<int> indexes = new List<int>();
string line = sr.ReadLine();
line = sr.ReadLine();
while (line != null)
{
all_list.Add(line);
string[] break1 = line.Split('/');
id_list.Add(break1[0]);
line = sr.ReadLine();
}
}
string input_id = textBox1.Text;
Data in the file:
client id/drum id
-----------------
123/321
231/3213
321/213123 ...
If the requirement was to use binary search I would create a custom class with a comparer, and then find an element and loop forward/backward to get any other elements. Like:
static void Main(string[] args
{
var path = #"file path...";
// read all the Ids from the file.
var id_list = File.ReadLines(path).Select(x => new Drum
{
ClientId = x.Split('/').First(),
DrumId = x.Split('/').Last()
}).OrderBy(o => o.ClientId).ToList();
var find = new Drum { ClientId = "231" };
var index = id_list.BinarySearch(find, new DrumComparer());
if (index != -1)
{
List<Drum> matches = new List<Drum>();
matches.Add(id_list[index]);
//get previous matches
for (int i = index - 1; i > 0; i--)
{
if (id_list[i].ClientId == find.ClientId)
matches.Add(id_list[i]);
else
break;
}
//get forward matches
for (int i = index + 1; i < id_list.Count; i++)
{
if (id_list[i].ClientId == find.ClientId)
matches.Add(id_list[i]);
else
break;
}
}
}
public class Drum
{
public string DrumId { get; set; }
public string ClientId { get; set; }
}
public class DrumComparer : Comparer<Drum>
{
public override int Compare(Drum x, Drum y) =>
x.ClientId.CompareTo(y.ClientId);
}
If i understand you question right then this should be a simple where stats.
// read all the Ids from the file.
var Id_list = File.ReadLines(path).Select(x => new {
ClientId = x.Split('/').First(),
DrumId = x.Split('/').Last()
}).ToList();
var foundIds = Id_list.Where(x => x.ClientId == input_id);

How can I access multi-element List data stored in a public class?

My first question on SO:
I created this public class, so that I can store three elements in a list:
public class myMultiElementList
{
public string Role {get;set;}
public string Country {get;set;}
public int Commonality {get;set;}
}
In my main class, I then created a new list using this process:
var EmployeeRolesCountry = new List<myMultiElementList>();
var rc1 = new myMultiElementList();
rc1.Role = token.Trim();
rc1.Country = country.Trim();
rc1.Commonality = 1;
EmployeeRolesCountry.Add(rc1);
I've added data to EmployeeRolesCountry and have validated that has 472 lines. However, when I try to retrieve it as below, my ForEach loop only retrieves the final line added to the list, 472 times...
foreach (myMultiElementList tmpClass in EmployeeRolesCountry)
{
string d1Value = tmpClass.Role;
Console.WriteLine(d1Value);
string d2Value = tmpClass.Role;
Console.WriteLine(d2Value);
int d3Value = tmpClass.Commonality;
Console.WriteLine(d3Value);
}
This was the most promising of the potential solutions I found on here, so any pointers greatly appreciated.
EDIT: adding data to EmployeeRolesCountry
/*
Before this starts, data is taken in via a csvReader function and parsed
All of the process below is concerned with two fields in the csv
One is simply the Country. No processing necessary
The other is bio, and this itself needs to be parsed and cleansed several times to take roles out
To keep things making sense, I've taken much of the cleansing out
*/
private void File_upload_Click(object sender, EventArgs e)
{
int pos = 0;
var EmployeeRolesCountry = new List<myMultiElementList>();
var rc1 = new myMultiElementList();
int a = 0;
delimiter = ".";
string token;
foreach (var line in records.Take(100))
{
var fields = line.ToList();
string bio = fields[5];
string country = fields[4];
int role_count = Regex.Matches(bio, delimiter).Count;
a = bio.Length;
for (var i = 0; i < role_count; i++)
{
//here I take first role, by parsing on delimiter, then push back EmployeeRolesCountry with result
pos = bio.IndexOf('.');
if (pos != -1)
{
token = bio.Substring(0, pos);
string original_token = token;
rc1.Role = token.Trim();
rc1.Country = country.Trim();
rc1.Commonality = 1;
EmployeeRolesCountry.Add(rc1);
a = original_token.Length;
bio = bio.Remove(0, a + 1);
}
}
}
}
EDIT:
When grouped by multiple properties, this is how we iterate through the grouped items:
var employeesGroupedByRolwAndCountry = EmployeeRolesCountry.GroupBy(x => new { x.Role, x.Country });
employeesGroupedByRolwAndCountry.ToList().ForEach
(
(countryAndRole) =>
{
Console.WriteLine("Group {0}/{1}", countryAndRole.Key.Country, countryAndRole.Key.Role);
countryAndRole.ToList().ForEach
(
(multiElement) => Console.WriteLine(" : {0}", multiElement.Commonality)
);
}
);
__ ORIGINAL POST __
You are instantiating rc1 only once (outside the loop) and add the same instance to the list.
Please make sure that you do
var rc1 = new myMultiElementList();
inside the loop where you are adding the elements, and not outside.
All references are the same in your case:
var obj = new myObj();
for(i = 0; i < 5; i++)
{
obj.Prop1 = "Prop" + i;
list.Add(obj);
}
now the list has 5 elements, all pointing to the obj (the same instance, the same object in memory), and when you do
obj.Prop1 = "Prop" + 5
you update the same memory address, and all the pointers in the list points to the same instance so, you are not getting 472 copies of the LAST item, but getting the same instance 472 times.
The solution is simple. Create a new instance every time you add to your list:
for(i = 0; i < 5; i++)
{
var obj = new myObj();
obj.Prop1 = "Prop" + i;
list.Add(obj);
}
Hope this helps.

Most efficient structure for quick access to a random element + the last element on one criteria

We have a function that is informed that we received an item for a specific timestamp.
The purpose of this is to wait that for one specific timestamp, we wait that we receive every item that we are expecting, then push the notification further once we are "synchronized" with all items.
Currently, we have a Dictionary<DateTime, TimeSlot> to store the non-synchronized TimeSlot(TimeSlot = list of all items we received for a specific timestamp).
//Let's assume that this method is not called concurrently, and only once per "MyItem"
public void HandleItemReceived(DateTime timestamp, MyItem item){
TimeSlot slot;
//_pendingTimeSlot is a Dictionary<DateTime,TimeSlot>
if(!_pendingTimeSlot.TryGetValue(timestamp, out slot)){
slot = new TimeSlot(timestamp);
_pendingTimeSlot.Add(timestamp,slot );
//Sometimes we don't receive all the items for one timestamps, which may leads to some ghost-incomplete TimeSlot
if(_pendingTimeSlot.Count>_capacity){
TimeSlot oldestTimeSlot = _pendingTimeSlot.OrderBy(t=>t.Key).Select(t=>t.Value).First();
_pendingTimeSlot.Remove(oldestTimeSlot.TimeStamp);
//Additional work here to log/handle this case
}
}
slot.HandleItemReceived(item);
if(slot.IsComplete){
PushTimeSlotSyncronized(slot);
_pendingTimeSlot.Remove(slot.TimeStamp);
}
}
We have severals instances of this "Synchronizer" in parallels for differents group of items.
It's working fine, except when the system is under heavy loads, we have more incomplete TimeSlot, and the application uses a lot more CPU. The profiler seems to indicate that the Compare of the LINQ query is taking a lot of time(most of the time). So I'm trying to find some structure to hold those references(replace the dictionary)
Here are some metrics:
We have several(variable, but between 10 to 20) instances of this Synchronizer
The current maximum capacity(_capacity) of the synchronizer is 500 items
The shortest interval that we can have between two different timestamp is 100ms(so 10 new Dictionary entry per seconds for each Synchronizer)(most case are more 1 item/second)
For each timestamp, we expect to receive 300-500 items.
So we will do, for one Synchronizer, per second(worst case):
1 Add
500 Get
3-5 Sorts
What would be my best move? I thought to the SortedDictionary But I didn't find any documentation showing me how to take the first element according to the key.
The first thing you can try is eliminating the OrderBy - all you need is the minimum key, no need to sort for getting that:
if (_pendingTimeSlot.Count > _capacity) {
// No Enumerable.Min(DateTime), so doing it manually
var oldestTimeStamp = DateTime.MaxValue;
foreach (var key in _pendingTimeSlot.Keys)
if (oldestTimeStamp > key) oldestTimestamp = key;
_pendingTimeSlot.Remove(oldestTimeStamp);
//Additional work here to log/handle this case
}
What about SortedDictionary, it is an option for sure, although it will consume much more memory. Since it's sorted, you can use simply sortedDictionary.First() to take the key value pair with the minimum key (hence the oldest element in your case).
UPDATE: Here is a hybrid approach using dictionary for fast lookups and ordered double linked list for the other scenarios.
class MyItem
{
// ...
}
class TimeSlot
{
public readonly DateTime TimeStamp;
public TimeSlot(DateTime timeStamp)
{
TimeStamp = timeStamp;
// ...
}
public bool IsComplete = false;
public void HandleItemReceived(MyItem item)
{
// ...
}
// Dedicated members
public TimeSlot PrevPending, NextPending;
}
class Synhronizer
{
const int _capacity = 500;
Dictionary<DateTime, TimeSlot> pendingSlotMap = new Dictionary<DateTime, TimeSlot>(_capacity + 1);
TimeSlot firstPending, lastPending;
//Let's assume that this method is not called concurrently, and only once per "MyItem"
public void HandleItemReceived(DateTime timeStamp, MyItem item)
{
TimeSlot slot;
if (!pendingSlotMap.TryGetValue(timeStamp, out slot))
{
slot = new TimeSlot(timeStamp);
Add(slot);
//Sometimes we don't receive all the items for one timestamps, which may leads to some ghost-incomplete TimeSlot
if (pendingSlotMap.Count > _capacity)
{
// Remove the oldest, which in this case is the first
var oldestSlot = firstPending;
Remove(oldestSlot);
//Additional work here to log/handle this case
}
}
slot.HandleItemReceived(item);
if (slot.IsComplete)
{
PushTimeSlotSyncronized(slot);
Remove(slot);
}
}
void Add(TimeSlot slot)
{
pendingSlotMap.Add(slot.TimeStamp, slot);
// Starting from the end, search for a first slot having TimeStamp < slot.TimeStamp
// If the TimeStamps almost come in order, this is O(1) op.
var after = lastPending;
while (after != null && after.TimeStamp > slot.TimeStamp)
after = after.PrevPending;
// Insert the new slot after the found one (if any).
if (after != null)
{
slot.PrevPending = after;
slot.NextPending = after.NextPending;
after.NextPending = slot;
if (slot.NextPending == null) lastPending = slot;
}
else
{
if (firstPending == null)
firstPending = lastPending = slot;
else
{
slot.NextPending = firstPending;
firstPending.PrevPending = slot;
firstPending = slot;
}
}
}
void Remove(TimeSlot slot)
{
pendingSlotMap.Remove(slot.TimeStamp);
if (slot.NextPending != null)
slot.NextPending.PrevPending = slot.PrevPending;
else
lastPending = slot.PrevPending;
if (slot.PrevPending != null)
slot.PrevPending.NextPending = slot.NextPending;
else
firstPending = slot;
slot.PrevPending = slot.NextPending = null;
}
void PushTimeSlotSyncronized(TimeSlot slot)
{
// ...
}
}
Some additional usages:
Iterating from oldest to newest:
for (var slot = firstPending; slot != null; slot = slot.NextPending)
{
// do something
}
Iterating from oldest to newest and removing items based on a criteria:
for (TimeSlot slot = firstPending, nextSlot; slot != null; slot = nextSlot)
{
nextSlot = slot.NextPending;
if (ShouldRemove(slot))
Remove(slot);
}
Same for reverse scenarios, but using lastPending and PrevPending members instead.
Here is simple sample. The insert method in a list eliminates swapping elements.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
List<Data> inputs = new List<Data>() {
new Data() { date = DateTime.Parse("10/22/15 6:00AM"), data = "abc"},
new Data() { date = DateTime.Parse("10/22/15 4:00AM"), data = "def"},
new Data() { date = DateTime.Parse("10/22/15 6:30AM"), data = "ghi"},
new Data() { date = DateTime.Parse("10/22/15 12:00AM"), data = "jkl"},
new Data() { date = DateTime.Parse("10/22/15 3:00AM"), data = "mno"},
new Data() { date = DateTime.Parse("10/22/15 2:00AM"), data = "pqr"},
};
Data data = new Data();
foreach (Data input in inputs)
{
data.Add(input);
}
}
}
public class Data
{
public static List<Data> sortedData = new List<Data>();
public DateTime date { get; set; }
public string data { get; set;}
public void Add(Data newData)
{
if(sortedData.Count == 0)
{
sortedData.Add(newData);
}
else
{
Boolean added = false;
for(int index = sortedData.Count - 1; index >= 0; index--)
{
if(newData.date > sortedData[index].date)
{
sortedData.Insert(index + 1, newData);
added = true;
break;
}
}
if (added == false)
{
sortedData.Insert(0, newData);
}
}
}
}
}​

Categories