Update list with a item modification with linq - c#

Suppose i have a list
List<ABCClass> lstABC; // it has some data
now need to modify a item from list like
var item = lstABC.Where(a=>a.index == 1).FirstOrDefault();
if(item != null)
item.Number = 5;
now what is the most efficient way to assign this item in lstABC again because i need updated lstABC without creating new list and assign to it.

As JohnB pointed out in the comments, if ABCClass really is a class (and not a struct), then item refers to the object in the list, and any change to it will be visible next time you run through the list.
However, if ABCClass really is a struct, then item is just a copy of the object in the list, and changes to it will not be reflected in the list.
if that case (which is unlikely), you'd need to do something list this:
List<int> lstABC; // it has some data
var idx = lstABC.FindIndex(a => a.index == 1);
if (idx != -1)
lstABC[idx].Number = 5;

Related

Compare Sharepoint List with C#

I am trying to compare two Sharepoint lists. I am using a C# program to add, update, and delete items, based on its ID. If the ID doesnt exist in List1, when the program is ran, I want to delete the IDs from List2. I was wondering how can I delete those items without specifying a specific number in the GetItemById function? Like in this example
using(ClientContext context = new ClientContext(siteUrl)) {
//Retrieve list items from list 1 here code here
using(ClientContext target = new ClientContext(siteUrl2)) {
foreach(ListItem oListItem2 in collListItem2) {
int exists = 0;
foreach(ListItem oListItem in collListItem) {
if (oListItem2["ID"] == oListItem["ID"]) {
exists++;
}
}
if (exists == 0) {
ListItem DeleteItem = list2.GetItemById();
DeleteItem.DeleteObject();
target.ExecuteQuery();
}
return;
}
}
}
To delete the items from the second list not in the first, just get all of the items from the first list, and filter the items in the second list based on those ids. Note you can use a hash based lookup to greatly improve performance over a linear search:
var idsFromFirstList = new HashSet<int>(
collListItem.AsEnumerable()
.Select(item => item.Id));
var itemsToDelete = collListItem2.AsEnumerable()
.Where(item => !idsFromFirstList.Contains(item.Id);
foreach(var item in itemsToDelete)
item.DeleteObject();
target.ExecuteQuery();
Note that you can basically do the exact opposite to find the items to add (create a hashset of the IDs of the items in the target into a set, find all items in the first no tin there, and then add all of those items).
To find items that match you can use a Dictionary<int, ListItem> by putting either set of items into a dictionary, with the ID as the key, and going through the other set, finding the matches. If you are going to do that, you can re-use that dictionary to check for one of the other two conditions as well, to save you one data structure:
var firstSiteItemLookup = collListItem.AsEnumerable()
.ToDictionary(item => item.Id, item => item);
foreach(var item in collListItem2)
{
ListItem match;
if(firstSiteItemLookup.TryGetValue(item.Id, out match))
UpdateItemToMatch(item, match);
else
item.DeleteObject();
}
target.ExecuteQuery();
Can you use the same code to check if an ID in collListItem has been modified then update the same ID in collListItem2? #Servy

Group, Sort, and then Merge to a Observable Collection?

I have a List of Items that have a "DisplayOrder" property. This can either have NULL or int value. If it has int value, it has priority and should be in the 1st group of the Observable Collection. Items in the 1st group are also sorted by DisplayOrder.
If it is NULL, then it belongs to the 2nd group, sorted alphabetically.
The 1st group and 2nd group are then combined for a Main Items Collection Observable Collection which I bind to a ListView.
This is my current code though I am worried if there is a much optimal way of doing it.
var MainItemCollection = new ObservableCollection<MainItemViewModel>();
var ItemsWithProperty = new ObservableCollection<MainItemViewModel>();
var ItemsWithNullProperty = new ObservableCollection<MainItemViewModel>();
foreach (var item in DataObject.MainItems)
{
if (item.DisplayOrder == null)
ItemsWithNullProperty.Add(new MainItemViewModel(item));
else
ItemsWithProperty.Add(new MainItemViewModel(item));
}
ItemsWithProperty = new ObservableCollection<MainItemViewModel>(ItemsWithProperty.OrderBy(c => c.DisplayOrder));
ItemsWithNullProperty = new ObservableCollection<MainItemViewModel>(ItemsWithNullProperty.OrderBy(c => c.Title));
//Add those with priorities first sorted by DisplayOrder 1,2,3,4
foreach (var c in ItemsWithProperty)
{
MainItemCollection.Add(c);
}
//Add those without priority sorted Alphabetically
foreach (var c in ItemsWithNullProperty)
{
MainItemCollection.Add(c);
}
Thank you!
Get the items with DisplayOrder=null & order them by Title:
ItemsWithNullProperty=DataObject.MainItems.Where(x=>x.DisplayOrder==null).OrderBy(o=>o.Title).ToList();
Get the items with DisplayOrder(all items except the above query) & order them by DisplayOrder:
ItemsWithProperty= DataObject.MainItems.Except(ItemsWithNullProperty).OrderBy(o=>o.DisplayOrder).ToList();
Fill the data in MainCollection:
var allItems = MainItemCollection.Concat(ItemsWithProperty)
.Concat(ItemsWithNullProperty)
.ToList();
When doing things like this, you don't need all those intermediate ObservableCollections - you can use the appropriate data structures like array, list, dictionary, hash set etc. or Linq queries. In this particular case, the whole procedure can be reduced to something like this
var MainItemCollection = new ObservableCollection<MainItemViewModel>(DataObject.MainItems
.OrderBy(item => item.DisplayOrder ?? int.MaxValue)
.ThenBy(item => item.DisplayOrder == null ? item.Title : string.Empty)
);
I feel that this is a pretty common scenario.
There is a sorted ObservableCollection bound to some XAML UI, and once more data is available UI needs to be updated without full refresh.
Whenever new ObservableCollection is created like in suggestions above, all items will be rebound and therefore UI fully updated.
I'm surprised that there are no library methods to achieve this. Here is the solution I've came up with. Hope someone might find it useful.
public static class ObservableCollectionExtensions
{
public static void MergeSortedListIntoSortedObservableCollection<T>(this ObservableCollection<T> destination, IList<T> list, Func<T, T, int> compareFunc)
{
int dest_index = 0;
int list_index = 0;
while (list_index < list.Count)
{
if (dest_index >= destination.Count)
{
destination.Add(list[list_index]);
list_index++;
dest_index++;
}
else
{
if (compareFunc(destination[dest_index], list[list_index]) > 0)
{
destination.Insert(dest_index, list[list_index]);
list_index++;
dest_index++;
}
else
{
dest_index++;
}
}
}
}
}

C# index list of objects

How do I use an index in C# in order to keep track of a list of questions?
The list has five questions, so that when the first one is loaded on the quiz page, the user provides an answer with the click of a button, which then goes to the answer page, which tells the user if they were right or wrong. On the answer page the user will click a "next question" button, which should load the next question in the list back on the quiz page. This should some how keep the index , so that the code knows that the second question is now to be loaded.
This gets the full list of questions:
public static IEnumerable<QuizGroups> GetGroups(string sectorId)
{
var Quizes = _QuizDataSource.AllQuizGroups.Where(x => x.Subtitle == sectorId);
return Quizes;
}
How do I use an index to iterate through the list of questions? There are 5 questions in the list.
There are 5 questions in the list.
You don't return a List<QuizGroups> but an IEnumerable<QuizGroups>. That's probably an "unmaterialized" query and not a collection. Otherwise you could use it's indexer directly to acces an item.
If there are only 5 items returned you could use ToList to create a real list. If you only need 5 and you want to ensure that, you can use Take(5) before:
public static IList<QuizGroups> GetGroups(string sectorId)
{
var Quizes = _QuizDataSource.AllQuizGroups
.Where(x => x.Subtitle == sectorId)
.OrderBy(x => CreatedAt)
.Take(5)
.ToList();
return Quizes;
}
Now you can use the indexer or Enumerable.ElementAt:
IList<QuizGroups> groups = GetGroups(sectorID);
QuizGroups quiz1 = groups[0]; // via indexer
QuizGroups quiz2 = groups.ElementAt(1); // via ElementAt, can throw an exception if there are less than 2
QuizGroups quiz3 = groups.ElementAt(2); // via ElementAtOrDefault, null if there are less than 3
// ...
Note that you don't need to use ToList in GetGroups to create a collection first. You could use ElementAt directly, but then you would always execute the query when you access the next index.
Edit according to your new background informations: I assume that you need to order the list by a DateTime property to ensure the correct order. Therefore i've added OrderBy in the query in GetGroups.
Do you need the index for anything else in your code.
If not, I would use a foreach
foreach(var quizGroup in GetGroups(1))
{
//you now have a reference to each quiz group as you're going through the list
}
Or for individual access
var q = GetGroups(1).ToList();
q[0]
q[1]
...
q[q.Count - 1]
Get a list from the Quizes with
var quizesList = Quizez.ToList();
and then:
for(int index = 0; index < quizesList.Count(); index++)
{
//Access Item at Index with quizesList[index];
}
or you can use ElementAt(int index)
for(int index = 0; index < Quizes.Count(); index++)
{
//Access Item at Index with Quizes.ElementAt(index);
}

Can not find index of combobox item when using a List as datasource

I'm populating a list from a LINQ Query, then I am using the list as the data source for a combo box.
I am then trying to select items within the combo box but it doesn't seem to be able to find the index
using (var db = new CruxEntities())
{
var query = from q in db.Businesses
where q.BusinessID == BusinessId
select q;
var B = query.FirstOrDefault();
if (B != null)
{
// other form controls populated
var sites = B.TblBusinessSites.ToList();
this.comboBox.DisplayMember = "SiteName";
this.comboBox.ValueMember = "BusinessSiteID";
this.comboBox.DataSource = sites;
int index = comboBox.FindString(B.IdFromLinq);
}
}
index always has the value of -1 assigned to it but i've stepped through the code and I know that the value exists in the list... it seems to be that the rest of the code is not recognising that the combo box has the values.
What am I missing?
Edit
I got the index fine... but something I missed out from my initial post is that there are 2 combo boxes bound to the list... I get the indexes for both of them fine when I step through the code but the combo boxes seem linked so I assigning the index to either one seems to assign it to both...
index = sites.FindIndex(s => s.BusinessSiteID == B.PrimarySiteDeliveryID);
comboBox_DefaultDeliverySite.SelectedIndex = index;
index = sites.FindIndex(s => s.BusinessSiteID == B.PrimarySiteInvoiceID);
comboBox_DefaultInvoiceSite.SelectedIndex = index;
You can't find site index because FindString method checks item's displayed text (site name in your case), but you are trying to search by ID, which is item's value.
Actually you even don't need to touch combobox here, because items will be added in same order as you have them in sites collection. To get index of some site you can just search index of site in sites list:
int index = sites.FindIndex(s => s.BusinessSiteID == B.IdFromLinq);
The FindString method is not a good choice, since you are binding with objects, so stick to object, not string.
You can loop on your item instead :
foreach(var item in comboBox.Items)
{
var businessSite = item as BusinessSite;
if(businessSite != null && businessSite.BusinessSiteID == B.IdFromLinq)
{
// your item here
}
}
Or index based:
for(int index = 0; index < comboBox.Items.Count; index++)
{
var item = comboBox.Items[index];
var businessSite = item as BusinessSite;
if(businessSite != null && businessSite.BusinessSiteID == B.IdFromLinq)
{
return index;
}
}

Why list show strange behaviour?

Work on Vs2010 EF,C#.
Have two list(oLisTranItem,oListTaxItem) ,need to copy one list properties values in another list ,then I need to work on new list.Problem is after copy content element one list to another list any type of changes impact on both list ,why this happen ,I just change on list but changes occur in both list
Please check my bellow syntax.
List<TransactionItem> oLisTranItem = new List<TransactionItem>();
List<TransactionItem> oListTaxItem = new List<TransactionItem>();
oLisTranItem = _TransactionItem;
oListTaxItem = _TransactionItemTax;
TransactionItem tmpItem = new TransactionItem();
tmpItem = oLisTranItem.Where(item => item.QuotationDetailID == quotationDetailID && item.Action != Entity.ActionMode.Delete && item.IsDeleted == false).FirstOrDefault();
if (tmpItem.IsNotNull())
{
tmpItem.Action = Entity.ActionMode.Add;
oListTaxItem.Add(tmpItem);
}
else
{
_TransactionItemTax = new List<TransactionItem>();
}
int nCounter = 5;
foreach (TransactionItem item in oListTaxItem)
{
if (item.QuotationTaxID ==0)
{
nCounter = nCounter + 1;
item.QuotationTaxID = nCounter;
}
}
Please help me to identify why this problem aries,how to solve this problem.
If have any query please ask,Thanks in advanced.
TransactionItem is probably a class, right? And not a struct.
Every type that's a class is, by default, a reference type. That means what you have in the lists are not the real values of the transaction items, but references (think C++ pointers) to those values. So when you copy data from one list to the other, you're just copying references.
You need to clone the items from one list to another. Give your class a method to clone instances, and use that method to copy items from one list to another.

Categories