I have this following Codes for adding a value in my List.
public class Temp {
public object Id { get; set; }
public object Amount { get; set; }
public object TrasactionDateTime { get; set; }
}
private List<Temp> list = new List<Temp>();
Adding
list.Add(new Temp{ Id = GetData["Id"], Amount = GetData["Amount"], TrasactionDateTime = GetData["TransactionDateTime"] });
How do i remove and item in the list?
example
list.Remove(Id = "1");
You need to find the item from the List with Id = "1" and then remove it.
var item = list.FirstOrDefault(r=> r.Id.ToString() == "1");
if(item != null)
list.Remove(item);
You can also remove the item based on the index using List<T>.RemoveAt()
Try to use the .Find
The first element that matches the conditions defined by the specified
predicate, if found; otherwise, the default value for type T.
List<Temp> list = new List<Temp>();
var f = list.Find(c => c.Id == 1);
if (f == null) return;
var x = list.Remove(f);
list.RemoveAll(s => s.Id == "1");
list.RemoveAll (s => s.Id == "1"); // remove by condition
Note that this will remove all temps with given id.
If you need to remove the first temp found by id, first use First method to find him, and then call Remove for the instance:
var firstMatch = list.First (s => s.Id == "1");
list.Remove (firstMatch);
If you want to ensure there is only one temp with given id before removing him, use Single in a similar fashion:
var onlyMatch = list.Single (s => s.Id == "1");
list.Remove (onlyMatch);
Note that Single call fails if there is not exactly one item matching the predicate.
Related
Title could be misleading, so an example:
I have a class:
class Pair
{
Book Book1;
Book Book2;
}
I have a list of these:
var list = new List<Pair>();
list.Add(new Pair() {
Book1 = new Book() { Id = 123 },
Book2 = new Book() { Id = 456 }
});
list.Add(new Pair() {
Book1 = new Book() { Id = 456 },
Book2 = new Book() { Id = 123 }
});
Now, despite the fact the books are 'flipped', my system should treat these as duplicates.
I need a method to remove one of these 'duplicates' from the list (any one - so let's say the first to make it simple).
What I've Tried
var tempList = new List<Pair>();
tempList.AddRange(pairs);
foreach (var dup in pairs)
{
var toRemove = pairs.FirstOrDefault(o => o.Book1.Id == dup.Book2.Id
&& o.Book2.Id == dup.Book1.Id);
if (toRemove != null)
tempList.Remove(toRemove);
}
return tempList;
This returns no items (given the example above), as both Pair objects would satisfy the condition in the lambda, I only one to remove one though.
NOTE: This wouldn't happen if I just removed the element from the collection straight away (rather than from a temporary list) - but then I wouldn't be able to iterate over it without exceptions.
You can set up an IEqualityComparer<Pair> concrete class and pass that to the .Distinct() method:
class PairComparer : IEqualityComparer<Pair>
{
public bool Equals(Pair x, Pair y)
{
return (x.Book1.Id == y.Book1.Id && x.Book2.Id == y.Book2.Id)
|| (x.Book1.Id == y.Book2.Id && x.Book2.Id == y.Book1.Id);
}
public int GetHashCode(Pair obj)
{
return obj.Book1.Id.GetHashCode() ^ obj.Book2.Id.GetHashCode();
}
}
And then use it like so:
var distinctPairs = list.Distinct(new PairComparer());
The problem is that you are removing the both duplicates.
Try this:
var uniquePairs = list.ToLookup( p => Tuple.Create(Math.Min(p.Book1.Id, p.Book2.Id), Math.Max(p.Book1.Id, p.Book2.Id)) ).Select( g => g.First() ).ToList();
I would use the following
foreach (var dup in pairs)
{
var toRemove = pairs.FirstOrDefault(o => o.Book1.Id == dup.Book2.Id
&& o.Book2.Id == dup.Book1.Id
&& o.Book1.Id > o.Book2.Id);
if (toRemove != null)
tempList.Remove(toRemove);
}
This will specifically remove the duplicate that is "out of order". But this (and your original) will fail if the duplicate pairs have the books in the same order.
A better solution (since we're looping over ever pair anyways) would be to use a HashSet
var hashSet = new HashSet<Tuple<int,int>>();
foreach (var item in pairs)
{
var tuple = new Tuple<int,int>();
if (item.Book1.Id < item.Book2.Id)
{
tuple.Item1 = item.Book1.Id;
tuple.Item2 = item.Book2.Id;
}
else
{
tuple.Item1 = item.Book2.Id;
tuple.Item2 = item.Book1.Id;
}
if (hashSet.Contains(tuple))
{
tempList.Remove(dup);
}
else
{
hashSet.Add(tuple);
}
}
I've managed to find a solution, but it's one I'm not happy with. It seems too verbose for the job I'm trying to do. I'm now doing an additional check to see whether a duplicate has already been added to the list:
if(toRemove != null && tempList.Any(o => o.Book1.Id == toRemove.Book2.Id
&& o.Book2.Id == toRemove.Book1.Id))
tempList.Remove(toRemove);
I'm very much open to alternative suggestions.
I've tried to search SO for solutions and questions that could be similar to my case.
I got 2 collections of objects:
public class BRSDocument
{
public string IdentifierValue { get; set;}
}
public class BRSMetadata
{
public string Value { get; set;}
}
I fill the list from my datalayer:
List<BRSDocument> colBRSDocuments = Common.Instance.GetBRSDocuments();
List<BRSMetadata> colBRSMetadata = Common.Instance.GetMessageBRSMetadata();
I now want to find that one object in colBRSDocuments where x.IdentifierValue is equal to the one object in colBRSMetadata y.Value. I just need to find the BRSDocument that matches a value from the BRSMetadata objects.
I used a ordinary foreach loop and a simple linq search to find the data and break when the value is found. I'm wondering if the search can be done completely with linq?
foreach (var item in colBRSMetadata)
{
BRSDocument res = colBRSDocuments.FirstOrDefault(x => x.IdentifierValue == item.Value);
if (res != null)
{
//Do work
break;
}
}
Hope that some of you guys can push me in the right direction...
Why not do a join?
var docs = from d in colBRSDocuments
join m in colBRSMetadata on d.IdentiferValue equals m.Value
select d;
If there's only meant to be one then you can do:
var doc = docs.Single(); // will throw if there is not exactly one element
If you want to return both objects, then you can do the following:
var docsAndData = from d in colBRSDocuments
join m in colBRSMetadata on d.IdentiferValue equals m.Value
select new
{
Doc = d,
Data = m
};
then you can access like:
foreach (var dd in docsAndData)
{
// dd.Doc
// dd.Data
}
Use Linq ?
Something like this should do the job :
foreach (var res in colBRSMetadata.Select(item => colBRSDocuments.FirstOrDefault(x => x.IdentifierValue == item.Value)).Where(res => res != null))
{
//Do work
break;
}
If you are just interested by the first item, then the code would be :
var brsDocument = colBRSMetadata.Select(item => colBRSDocuments.FirstOrDefault(x => x.IdentifierValue == item.Value)).FirstOrDefault(res => res != null);
if (brsDocument != null)
//Do Stuff
What I want to be able to do is search the searchValues list for duplicate itemId's, and when I find them, place the single string value into the string values array.
The SearchValue object:
public class SearchValue<TItemId>
{
public TItemId ItemId { get; set; }
public string Value { get; set; }
public string[] Values { get; set; }
}
My test search values after init looks like this:
searchValues[0]
.ItemId == 16
.Value == "2"
searchValues[1]
.ItemId == 16
.Value == "3"
searchValues[2]
.ItemId == 15
.Value == "6"
searchValues[3]
.ItemId == 15
.Value == "3"
searchValues[4]
.ItemId == 5
.Value == "Vertonghen"
I'd like my final result to look like this:
searchValues[0]
.ItemId == 16
.Values == "2,3"
searchValues[1]
.ItemId == 15
.Values == "6,3"
searchValues[2]
.ItemId == 5
.Value == "Vertonghen"
I'd really want to do this using LINQ. I've managed to create another List of SearchValue thusly:
List<SearchValue<Byte>> duplicateSearchItems = (from x in searchValues
group x by x.ItemId into grps
orderby grps.Key
where grps.Count() > 1
select grps).SelectMany(group => group).ToList();
...but getting the value into the values array is giving me trouble. Ideally if the LINQ could return a single list that contains the duplicate records transmogrified into the array with the non-duplicates intact would be best. Probably a nested query of some sort? I'm stumped. Any ideas?
Thanks in advance!
why don't you use Dictionary
Dictionary<int,string> d = new Dictionary<int,string>();
foreach(var x in searchValues)
{
if(d.ContainsKey(x.ItemId))
d[x.ItemId] = string.Format("{0},{1}",d[x.ItemId],x.Value);
else
d.Add(x.ItemId,x.Value);
}
on the end simply iterate throug the Dictionary
foreach(var entry in d)
{
ConsoleWriteline(entry.Key+" : "+entry.Value);
}
Linq alone cannot be used to modify the original list or to modify the items in the list. However, you could do this to create a new list:
List<SearchValue<Byte>> results =
(from x in searchValues
group x by x.ItemId into g
select new SearchValue<Byte>()
{
ItemId = g.Key,
Value = g.Value.First().Value,
Values = g.Value.Select(i => i.Value).ToArray(),
}
.ToList();
Or in fluent syntax:
List<SearchValue<Byte>> results = searchValues
.GroupBy(x => x.ItemId)
.Select(g => new SearchValue<Byte>()
{
ItemId = g.Key,
Value = g.Value.First().Value,
Values = g.Value.Select(i => i.Value).ToArray(),
})
.ToList();
However, depending on your situation, an ILookup may be more appropriate for you:
var results = searchValues.ToLookup(x => x.ItemId, x => x.Value);
Console.Write(String.Join(", ", results[16])); // 2, 16
I don't think LINQ would provide you with the best solution here. Similar to Nikola, I'd use a Dictionary. If you aren't married to your SearchValue data type, you can avoid the second loop that just pushes your data back into your type. A Dictionary> would work here.
var searchValues = new List<SearchValue<int>>();
var distinctItemIds = new Dictionary<int, List<string>>();
foreach (var item in searchValues)
{
if (!distinctItemIds.ContainsKey(item.ItemId))
{
distinctItemIds.Add(item.ItemId, new List<string>());
}
// Add the value
distinctItemIds[item.ItemId].Add(item.Value);
}
// Put values back into your data object
var finishedValues = new List<SearchValue<int>>();
foreach (var keyValuePair in distinctItemIds)
{
finishedValues.Add(new SearchValue<int>()
{
ItemId = keyValuePair.Key,
Values = keyValuePair.Value.ToArray()
});
}
I managed to work it out using LINQ.
// Get a new list of unique items to add our duplicated items to
List<SearchValue<Byte>> finalSearchItems = (from x in searchValues
group x by x.ItemId into grps
orderby grps.Key
where grps.Count() == 1
select grps).SelectMany(group => group).ToList();
byte[] duplicateIds = (from x in searchValues
group x by x.ItemId into grps
where grps.Count() > 1
select grps.Key).ToArray();
// Smash the string 'Value' into 'Values[]'
foreach (byte id in duplicateIds)
{
SearchValue<Byte> val = new SearchValue<byte>();
val.ItemId = id;
// Smash
val.Values = (from s in searchValues
where s.ItemId == id
select s.Value).ToArray();
finalSearchItems.Add(val);
}
I have a List<Item>. Item has properties Id,Name and Amount. There are duplicated items in this list. I need to get a new List<Item> which contains only non-duplicated Items and in Item's Amount should be the quantity of how many times it duplicated in first List<Item>. I tried something like
for (int i = 0; i < list.Count; i++)
{
for (int j = 0; j < list.Count; j++)
{
if (list[i].Name == list[j].Name)
{
list.Remove(prod.Components[j]);
list[i].Amount++;
}
}
}
but there are some problems in this loop. My brains are overheated. Please, help.
A simple LINQ query can get you the unique items along with the number of times they appear:
var distinct = list.GroupBy(o => o.Name)
.Select(g => new { Count = g.Count(), Item = g.First() })
.ToList();
Then you can modify each item's Amount to the count of duplicates:
foreach (var row in distinct)
{
row.Item.Amount = row.Count;
}
And finally get back a List<Item> that contains no duplicates and has the correct amounts:
var uniqueItems = distinct.Select(r => r.Item).ToList();
Important: The code above assumes that "duplicate" items are indistinguishable from each other, but nothing else (e.g. it doesn't need Item to have a default constructor). Depending on the particulars it may be possible to write it in an even shorter form.
Additionally, the Amount property looks strange here. Since duplicates do not warrant summation of their amounts, what's the purpose of Item.Amount? I would assume that duplicate items with amount of 2 should result in one item with an amount of 4, but your code does not do that (and mine follows that lead).
Off the top of my head (haven't tested it):
list.GroupBy(x => x.Name)
.Select(x => new Item {
Name = x.Key,
Amount = x.Count()
})
.ToList();
You haven't specified what happens to the Ids, so I've left ignored them.
(Note this creates a new list, rather than modifying the original).
Try something like that:
var groups = from item in items
group item by item.Property
into grouped
select grouped;
var distinct = from g in groups
let item = g.First()
let amount = g.Count()
select new Item {Property = item.Property, Amount = amount};
After that distinct contains IEnumerable<Item> with their amount from original items list.
foreach(var item in list)
{
if(list.Count(e=>e.Id == item.Id && e.Name == item.Name)!=1)
{
list.Remove(item);
}
}
Assuming that you determine duplicates by the first two properties ID and Name.
You can implement an IEqualityComparer<Item> and use that for Enumerable.GroupBy:
var itemAmounts = items.GroupBy(i => i, new Item())
.Select(g => new Item {
ID = g.First().ID,
Name = g.First().Name,
Amount = g.Count()
});
Here's your Item class with a meaningful implementation of IEqualityComparer<Item>:
class Item : IEqualityComparer<Item>
{
public int ID;
public string Name;
public int Amount;
public bool Equals(Item x, Item y)
{
if (x == null || y == null) return false;
bool equals = x.ID == y.ID && x.Name == y.Name;
return equals;
}
public int GetHashCode(Item obj)
{
if (obj == null) return int.MinValue;
int hash = 19;
hash = hash + obj.ID.GetHashCode();
hash = hash + obj.Name.GetHashCode();
return hash;
}
}
You could also override Equals and GetHasdhCode from object, then you don't need a custom comparer at all in GroupBy:
var itemAmounts = items.GroupBy(i => i)
.Select(g => new Item {
ID = g.First().ID,
Name = g.First().Name,
Amount = g.Count()
});
You can use above already available methods:
public override bool Equals(object obj)
{
Item item2 = obj as Item;
if (item2 == null)
return false;
else
return Equals(this, item2);
}
public override int GetHashCode()
{
return GetHashCode(this);
}
Here I have a simple example to find an item in a list of strings. Normally I use a for loop or anonymous delegate to do it like this:
int GetItemIndex(string search)
{
int found = -1;
if ( _list != null )
{
foreach (string item in _list) // _list is an instance of List<string>
{
found++;
if ( string.Equals(search, item) )
{
break;
}
}
/* Use an anonymous delegate
string foundItem = _list.Find( delegate(string item) {
found++;
return string.Equals(search, item);
});
*/
}
return found;
}
LINQ is new for me. Can I use LINQ to find an item in the list? If it is possible, how?
There are a few ways (note that this is not a complete list).
Single will return a single result, but will throw an exception if it finds none or more than one (which may or may not be what you want):
string search = "lookforme";
List<string> myList = new List<string>();
string result = myList.Single(s => s == search);
Note that SingleOrDefault() will behave the same, except it will return null for reference types, or the default value for value types, instead of throwing an exception.
Where will return all items which match your criteria, so you may get an IEnumerable<string> with one element:
IEnumerable<string> results = myList.Where(s => s == search);
First will return the first item which matches your criteria:
string result = myList.First(s => s == search);
Note that FirstOrDefault() will behave the same, except it will return null for reference types, or the default value for value types, instead of throwing an exception.
If you want the index of the element, this will do it:
int index = list.Select((item, i) => new { Item = item, Index = i })
.First(x => x.Item == search).Index;
// or
var tagged = list.Select((item, i) => new { Item = item, Index = i });
int index = (from pair in tagged
where pair.Item == search
select pair.Index).First();
You can't get rid of the lambda in the first pass.
Note that this will throw if the item doesn't exist. This solves the problem by resorting to nullable ints:
var tagged = list.Select((item, i) => new { Item = item, Index = (int?)i });
int? index = (from pair in tagged
where pair.Item == search
select pair.Index).FirstOrDefault();
If you want the item:
// Throws if not found
var item = list.First(item => item == search);
// or
var item = (from item in list
where item == search
select item).First();
// Null if not found
var item = list.FirstOrDefault(item => item == search);
// or
var item = (from item in list
where item == search
select item).FirstOrDefault();
If you want to count the number of items that match:
int count = list.Count(item => item == search);
// or
int count = (from item in list
where item == search
select item).Count();
If you want all the items that match:
var items = list.Where(item => item == search);
// or
var items = from item in list
where item == search
select item;
And don't forget to check the list for null in any of these cases.
Or use (list ?? Enumerable.Empty<string>()) instead of list.
Do you want the item in the list or the actual item itself (would assume the item itself).
Here are a bunch of options for you:
string result = _list.First(s => s == search);
string result = (from s in _list
where s == search
select s).Single();
string result = _list.Find(search);
int result = _list.IndexOf(search);
If it really is a List<string> you don't need LINQ, just use:
int GetItemIndex(string search)
{
return _list == null ? -1 : _list.IndexOf(search);
}
If you are looking for the item itself, try:
string GetItem(string search)
{
return _list == null ? null : _list.FirstOrDefault(s => s.Equals(search));
}
This method is easier and safer
var lOrders = new List<string>();
bool insertOrderNew = lOrders.Find(r => r == "1234") == null ? true : false
How about IndexOf?
Searches for the specified object and returns the index of the first occurrence within the list
For example
> var boys = new List<string>{"Harry", "Ron", "Neville"};
> boys.IndexOf("Neville")
2
> boys[2] == "Neville"
True
Note that it returns -1 if the value doesn't occur in the list
> boys.IndexOf("Hermione")
-1
This will help you in getting the first or default value in your LINQ List search
var results = _List.Where(item => item == search).FirstOrDefault();
This search will find the first or default value, which it will return.
I used to use a Dictionary which is some sort of an indexed list which will give me exactly what I want when I want it.
Dictionary<string, int> margins = new Dictionary<string, int>();
margins.Add("left", 10);
margins.Add("right", 10);
margins.Add("top", 20);
margins.Add("bottom", 30);
Whenever I wish to access my margins values, for instance, I address my dictionary:
int xStartPos = margins["left"];
int xLimitPos = margins["right"];
int yStartPos = margins["top"];
int yLimitPos = margins["bottom"];
So, depending on what you're doing, a dictionary can be useful.
If we need to find an element from the list, then we can use the Find and FindAll extensions method, but there is a slight difference between them. Here is an example.
List<int> items = new List<int>() { 10, 9, 8, 4, 8, 7, 8 };
// It will return only one 8 as Find returns only the first occurrence of matched elements.
var result = items.Find(ls => ls == 8);
// this will returns three {8,8,8} as FindAll returns all the matched elements.
var result1 = items.FindAll(ls => ls == 8);
Here is one way to rewrite your method to use LINQ:
public static int GetItemIndex(string search)
{
List<string> _list = new List<string>() { "one", "two", "three" };
var result = _list.Select((Value, Index) => new { Value, Index })
.SingleOrDefault(l => l.Value == search);
return result == null ? -1 : result.Index;
}
Thus, calling it with
GetItemIndex("two") will return 1,
and
GetItemIndex("notthere") will return -1.
Reference: linqsamples.com
Try this code:
return context.EntitytableName.AsEnumerable().Find(p => p.LoginID.Equals(loginID) && p.Password.Equals(password)).Select(p => new ModelTableName{ FirstName = p.FirstName, UserID = p.UserID });
You can use FirstOfDefault with the Where LINQ extension to get a MessageAction class from the IEnumerable. Reme
var action = Message.Actions.Where(e => e.targetByName == className).FirstOrDefault<MessageAction>();
where
List<MessageAction> Actions { get; set; }
One more way to check the existence of an element in a List<string>:
var result = myList.Exists(users => users.Equals("Vijai"))
You want to search an object in object list.
This will help you in getting the first or default value in your Linq List search.
var item = list.FirstOrDefault(items => items.Reference == ent.BackToBackExternalReferenceId);
or
var item = (from items in list
where items.Reference == ent.BackToBackExternalReferenceId
select items).FirstOrDefault();