Need help pulling certain data out of a list - c#

I have a call Center Application that displays calls in queue in a bunch of other data to our analysts and managers. I'm trying to display the next three people in the queue to receive a call. I was able to put together the code below and reference max3 as an itemsource in a listbox but it doesn't actually display the names of the people next. when you add a breakpoint on max3 it shows the three agents that are next but it also shows all of their data, time in queue, extension number, stuff like that. i need to know how to only display their name.
List<NewAgent> newAgentList = new List<NewAgent>();
List<Tuple<String, TimeSpan>> availInItems = new List<Tuple<string, TimeSpan>>();
foreach (var item in e.CmsData.Agents)
{
NewAgent newAgents = new NewAgent();
newAgents.AgentName = item.AgName;
newAgents.AgentExtension = item.Extension;
newAgents.AgentDateTimeChange = ConvertedDateTimeUpdated;
newAgents.AuxReasons = item.AuxReasonDescription;
newAgents.LoginIdentifier = item.LoginId;
newAgents.AgentState = item.WorkModeDirectionDescription;
var timeSpanSince = DateTime.Now - item.DateTimeUpdated;
newAgents.AgentDateTimeStateChange = timeSpanSince;
newAgentList.Add(newAgents);
if (item.WorkModeDirectionDescription == "AVAIL-IN")
{
availInItems.Add(Tuple.Create(newAgents.AgentName, timeSpanSince));
}
availInItems.Sort((t1, t2) => t1.Item2.CompareTo(t2.Item2));
}
Occurs after above code:
var availInAgents = newAgentList
.Where(ag => ag.AgentState == "AVAILIN") .ToList();
availInAgents.Sort((t1, t2) =>
t1.AgentDateTimeStateChange.CompareTo(t2.AgentDateTimeStateChange));
var minTimeSpanAgent = availInAgents.FirstOrDefault();
var maxTimeSpanAgent = availInAgents.LastOrDefault();
var min3 = availInAgents.Take(3).ToList();
var max3 = availInAgents.Skip(availInAgents.Count - 3);
max3.Reverse();
This is where my problem exists, it displays the info in the screen shot below. I only need the AgentName out of it and i don't know how to only access that piece of information. Please assist with this.
nextInLine.itemsource = max3.ToString();

Use the .Select() method to project a new type from your query.
nextInLine.itemsource = max3?.Select(x => x?.AgentName).FirstOrDefault() ?? string.Empty;
It will take the first agent in max3 and retrieve only the string AgentName, assigning it to itemsource.
The ? in this case is the null propagation operator. If max3 is null, it will return null before evaluating the .Select() which, in conjunction with the Null Coalescing operator (??), will set the itemsource to an empty string. It repeats this process if any item in your max3 list is null, or if AgentName itself is null.

you can use Select from Linq
var agentNamesFromMax3 = max3.Select(m => m.AgentName);

Related

Possible Multiple Enumeration Scenario. How to handle? [duplicate]

This question already has answers here:
Fastest way to get matching items from two list c#
(4 answers)
Closed 1 year ago.
public IEnumerable<ComputedData> Compute (IEnumerable<DataSet> data, IEnumerable<Variations> variationData)
{
// I need to create one ComputedData instance for each item inside the IEnumerable<DataSet> data
// DataSet has properties: int ID, int Valu1, int Value2
// ComputedData has properties: int ID, int Result, int Variation
// variationData has properties: int ID, int Variation
var computedDate = data.Select (i => new ComputedData ()
{
ID = i.ID,
Result = i.value1 + i.value2
});
// ISSUE is here
foreach (var item in computedDate )
{
var id = item.ID;
// I need to find the corresponding element
// (with same ID) on IEnumerable<Variations> variationData
// and assign item.Variation =
// But getting Possible Multiple Enumeration warning
// and item.Variation become zero always !
}
}
Issue is the use of foreach loop. Is there any other way to solve this issue. Even though the code is working, item.Variation is always zero. That shouldn't be the case.
Any suggestions ?
The warning is to ensure that every time you iterate through the items in computeDate you don't re-execute this:
var computedDate = data.Select(i => new ComputedData()
{
ID = i.ID,
Result = i.value1 + i.value2
}).
which could, in turn, re-execute whatever method produced IEnumerable<DataSet> data.
There are few ways to deal with that. An easy one is to add this:
var computedDate = data.Select(i => new ComputedData()
{
ID = i.ID,
Result = i.value1 + i.value2
}).ToArray(); // <---- Add this
After this line of code enumerates the items in data the results are stored in an array, which means you can safely enumerate it again and again without executing this Select or anything else unexpected.

How to set List child element with for each If it is empty initially?

I have Ilist to get all Offer from repository using entity framework core. Also I have service model OfferResponseModel which includes
OfferRequestModel as reference. I used mapster to bind entity model to service model. However it only set first child. Now I want to bind it manually. I created "offers" with the size of "Offer". When I try to use foreach loop, I cannot set "offers" child element.Because it has no elements. So, I can I solve this.
var offer = await _unitOfWork.Offers.GetAllOffer();
if (offer == null)
throw ServiceExceptions.OfferNotFound;
var results = new List<OfferResponseModel>(offer.Count);
results.ForEach(c => { c.Offer = new OfferRequestModel(); });
int i = 0;
foreach(var result in results)
{
result.Offer.User = Offer[i].User.Adapt<UserResponseModel>();
result.Offer.Responsible = Offer[i].Responsible.Adapt<EmployeeResponseModel>();
result.CreatedDate = Offer[i].CreatedDate;
result.ModifiedBy = Guid.Parse(Offer[i].UpdatedBy);
result.Active = Offer[i].Status;
result.Offer = Offer[i].Offer;
result.Offer.User.Company = Offer[i].Company.Adapt<CompanyModel>();
i++;
}
I created "offers" with the size of "Offer".
No, you created it with that capacity. It's still an empty list. It's not clear to me why you're trying to take this approach at all - it looks like you want one OfferResponseModel for each entry in offer, directly from that - which you can do with a single LINQ query. (I'm assuming that offer and Offer are equivalent here.)
var results = Offer.Select(o => new OfferResponseModel
{
Offer = new OfferRequestModel
{
User = o.User.Adapt<UserResponseModel>(),
Responsible = o.Responsible.Adapt<EmployeeResponseModel>()
},
CreatedDate = o.CreatedDate,
ModifiedBy = Guid.Parse(o.UpdatedBy),
Active = o.Status
}).ToList();
That doesn't set the Offer.User.Company in each entry, but your original code is odd as it sets the User and Responsible properties in the original Offer property, and then replaces the Offer with Offer[i].Offer. (Aside from anything else, I'd suggest trying to use the term "offer" less frequently - just changing the plural to "offers" would help.)
I suspect that with the approach I've outlined above, you'll be able to work out what you want and express it more clearly anyway. You definitely don't need to take the "multiple loops" approach of your original code.
One thing you have left out is the type of the offer variable that is referenced in the code. But I am thinking you need to do something along these lines:
if (offer == null)
throw ServiceExceptions.OfferNotFound;
var results = offer.Select(o => new OfferResponseModel
{
Offer = new OfferRequestModel
{
User = o.User.Adapt<UserResponseModel>(),
Responsible = o.Responsible.Adapt<EmployeeResponseModel>(),
...
}
}).ToList();
Select basically loops through any items in offer and "converts" them to other objects, in this case OfferResponseModel. So inside select you simply new up an OfferResponseModel and directly sets all the properties you need to set.
You need using System.Linq; for Select to be available.

Linq OrderBy not sorting correctly 100% of the time

I'm using the Linq OrderBy() function to sort a generic list of Sitecore items by display name, then build a string of pipe-delimited guids, which is then inserted into a Sitecore field. The display name is a model number of a product, generally around 10 digits. At first it seemed like this worked 100% of the time, but the client found a problem with it...
This is one example that we have found so far. The code somehow thinks IC-30R-LH comes after IC-30RID-LH, but the opposite should be true.
I put this into an online alphabetizer like this one and it was able to get it right...
I did try adding StringComparer.InvariantCultureIgnoreCase as a second parameter to the OrderBy() but it did not help.
Here's the code... Let me know if you have any ideas. Note that I am not running this OrderBy() call inside of a loop, at any scope.
private string GetAlphabetizedGuidString(Item i, Field f)
{
List<Item> items = new List<Item>();
StringBuilder scGuidBuilder = new StringBuilder();
if (i != null && f != null)
{
foreach (ID guid in ((MultilistField)f).TargetIDs)
{
Item target = Sitecore.Data.Database.GetDatabase("master").Items.GetItem(guid);
if (target != null && !string.IsNullOrEmpty(target.DisplayName)) items.Add(target);
}
// Sort it by item name.
items = items.OrderBy(o => o.DisplayName, StringComparer.InvariantCultureIgnoreCase).ToList();
// Build a string of pipe-delimited guids.
foreach (Item item in items)
{
scGuidBuilder.Append(item.ID);
scGuidBuilder.Append("|");
}
// Return string which is a list of guids.
return scGuidBuilder.ToString().TrimEnd('|');
}
return string.Empty;
}
I was able to reproduce your problem with the following code:
var strings = new string[] { "IC-30RID-LH", "IC-30RID-RH", "IC-30R-LH", "IC-30R-RH"};
var sorted = strings.OrderBy(s => s);
I was also able to get the desired sort order by adding a comparer to the sort.
var sorted = strings.OrderBy(s => s, StringComparer.OrdinalIgnoreCase);
That forces a character-by-character (technically byte-by-byte) comparison of the two strings, which puts the '-' (45) before the 'I' (73).

C# while loop overwriting scoped variable with each iteration

I have 2 lists, allCharges and selectedCharges, where each item in selectedCharges is contained in allCharges.
I want to create a third list (called charges) that is a copy of all charges, but with a boolean property of "Selected" for each item set to true if the charge is in the list of selected charges.
This is fine if I am only doing this once; however, I am trying to perform the same operation five times, rewriting "charges" each time while saving "charges" to my "typeSettingsList".
IList<TypeSettings> typeSettingsList = new List<TypeSettings>();
var typeId = 1;
while (typeId < 6)
{
var links = _linkChargeTypeRepo.Query().Where(l => l.TypeId == typeId);
var allCharges = _chargeRepo.GetAll().ToList();
var selectedCharges = links.Select(l => l.ChargeType).ToList();
selectedCharges.ForEach(c => c.Selected = true);
var nonSelectedCharges = allCharges.Except(selectedCharges).ToList();
nonSelectedCharges.ForEach(c => c.Selected = false);
var charges = nonSelectedCharges.Concat(selectedCharges).ToList();
var settingsWithType = new TypeSettings
{
Type = _typeRepo.Get(typeId),
Charges = charges
};
typeSettingsList.Add(settingsWithType);
typeId++;
}
return settingsWithType;
My problem is that each "Charges" object in my typeSettingsList ends up getting overwritten with the charges object that is created on the last iteration, even though the variable is declared inside the while loop (and should therefore be a new object reference with each iteration).
Is this just an incorrect understanding of how variables inside while loops should work?
How can I make it so my "charges" list isn't overwritten with each iteration?
The problem is that you do not "materializecharges`: this
var charges = nonSelectedCharges.Concat(selectedCharges);
is an IEnumerable<Charge> with deferred evaluation. By the time you get to evaluate Charges from the typeSettingsList, the loop is over, so enumerating the IEnumerable<Charge> returns the results for the last value of typeId (i.e. typeId = 5).
Add ToList() to fix this problem:
var charges = nonSelectedCharges.Concat(selectedCharges).ToList();
Edit: Another problem is that links is using typeId, which is modified in the loop. You should define a new variable inside the loop to capture the state of typeId during the specific iteration, like this:
var typeId = 1;
while (typeId < 6)
{
var tmpTypeId = typeId;
var links = _linkChargeTypeRepo.Query().Where(l => l.TypeId == tmpTypeId);
...
}

IQueryable Select returns 0 records

Hello I am failry new to silverlight and C# and have a program that utilizes the RIA techinique of using data. I am having problems with selecting a single column from my datasource. I am trying to use the value of that column to populate a single series in my chart.
On my UI I have a Grid and a bar chart.
I am able to populate my Grid by using:
DomainContext ctx = new DomainContext();
ListingGrid.ItemsSource = ctx.v_IQ_Flashes;
ctx.Load(ctx.Get_Table1());
That populates my datagrid with all(*) fields from my table Table1
Now I want to populate a single series on my chart with just one column from that chart.
Using the following code yeilds a return value of 0 (which is incorrect). What am I doing wrong?
var slot = ctx.v_IQ_Flashes.Where(e => e.Year == t_year).Select(e => e.Win );
var sum_ret_slot = slot.Sum();
decimal sum_slot = sum_ret_slot.Value;
Note that all values, (Slot, sum_ret_slot, sum_slot) are all 0. I used the debugger to verify that these are indeed being called and that their values are all 0.
It seems to me that you aren't actually getting anything from your Where clause.
Does removing the where clause cause it to work, it so I would evaluate that and determine why that isn't matching any records.
I agree with msarchet - check the value of your t_year variable - are there any rows which actually match your Where predicate?
Do you get results if you change the code to:
var slot = ctx.v_IQ_Flashes.Select(e => e.Win );
var sum_ret_slot = slot.Sum();
decimal sum_slot = sum_ret_slot.Value;
Or do you get results if you change the code to:
var slot = ctx.v_IQ_Flashes.Where(e => e.Year == 2010).Select(e => e.Win );
var sum_ret_slot = slot.Sum();
decimal sum_slot = sum_ret_slot.Value;
Where do you add code of sum_slot getting?
If you do it consequentially with loading, zero results are right cause ctx.Load(ctx.Get_Table1()) is async operation and if next your step is filter, data is not loaded yet.
I think, first way to make this code right - add filter to callback
ctx.Load(ctx.Get_Table1(), ()=>{//get sum_slot }, null);
Second way - apply filter to query:
ctx.Load(ctx.Get_Table1().Where(e => e.Year == t_year)) - context will load filtered items.

Categories