How can i remove same item in list - c#

Could you help me? I cant remove the same item in a list.
List<string> text = new List<string>();
text.Add("A");
text.Add("B");
text.Add("C");
text.Add("D");
text.Add("D");
text.Add("A");
foreach(string i in text)
{
Console.WriteLine(i);
}
the result is A,B,C,D,D,A but I need to be B,C . How can i do?

Here is my solution,
var result = text.GroupBy(x => x).Where(y => y.Count() == 1).Select(z => z.Key);
Console.WriteLine(string.Join(", ", result));
Explanation:
GroupBy(x => x): This will group list based on characters i.e predicate.
.Where(y => y.Count() == 1): This will filter elements which are duplicates.
.Select(z => z.Key): Select will create new enumerable which contains Keys from Grouped elements

Something like
text.GroupBy(t => t).Where(tg => tg.Count()==1).Select(td => td.First());
This proberly wont compile, you need to fix that your self.
The idea is:
1. Group by item.
2. Take all groups with exactly 1 item
3. Select the Item

Related

Filter a List of Strings and select only one occurance of a string with multiple occurance

var paymentTypes = _context
.BursaryTransactions
.Select(c => c.PaymentType)
.ToList();
string[] obj = paymentTypes
.ToArray();
var a = obj[1];
The first line retrieves a list of Payment type which are in strings from BursaryTransactions Table
the second line converts the list to array.
The list from the first line however contains Similar Strings for example
Post Utme
School Fee
School Fee
Post Utme
Hnd Form
Hnd Form
I want to filter these list and retrive just one occurrance of an item that appears more than once. then converts the new list to array.
You can try GroupBy and choose groups with more than 1 item:
var result = _context
.BursaryTransactions
.GroupBy(c => c.PaymentType) // Group By PaymentType
.Where(group => group.Count() > 1) // groups with more than 1 item
.Select(group => group.First()) // we want just the 1st item of such group
.ToList(); // materialized as a List<T>
Edit: to remove duplicates we can take First item from each group:
var result = _context
.BursaryTransactions
.GroupBy(c => c.PaymentType)
.Select(group => group.First()) // First().PaymentType if you want PaymentType only
.ToList();
You can try this way
var result = _context.BursaryTransactions.GroupBy(c => c.PaymentType)
.Where(g => g.Count() > 1)
.Select(g => g.First())
.ToArray();

How to do in this in Linq C#

So far, I have this:
var v = Directory.EnumerateFiles(_strConfigurationFolder)
.GroupBy(x => GetReportName(Path.GetFileNameWithoutExtension(x)));
Configuration folder will contain pairs of files:
abc.json
abc-input.json
def.json
def-input.json
GetReportName() method strips off the "-input" and title cases the filename, so you end up with a grouping of:
Abc
abc.json
abc-input.json
Def
def.json
def-input.json
I have a ReportItem class that has a constructor (Name, str1, str2). I want to extend the Linq to create the ReportItems in a single statement, so really something like:
var v = Directory.EnumerateFiles(_strConfigurationFolder)
.GroupBy(x => GetReportName(Path.GetFileNameWithoutExtension(x)))
**.Select(x => new ReportItem(x.Key, x[0], x[1]));**
Obviously last line doesn't work because the grouping doesn't support array indexing like that. The item should be constructed as "Abc", "abc.json", "abc-input.json", etc.
If you know that each group of interest contains exactly two items, use First() to get the item at index 0, and Last() to get the item at index 1:
var v = Directory.EnumerateFiles(_strConfigurationFolder)
.GroupBy(x => GetReportName(Path.GetFileNameWithoutExtension(x)))
.Where(g => g.Count() == 2) // Make sure we have exactly two items
.Select(x => new ReportItem(x.Key, x.First(), x.Last()));
var v = Directory.EnumerateFiles(_strConfigurationFolder)
.GroupBy(x => GetReportName(Path.GetFileNameWithoutExtension(x))).Select(x => new ReportItem(x.Key, x.FirstOrDefault(), x.Skip(1).FirstOrDefault()));
But are you sure there will be exactly two items in each group? Maybe has it sence for ReportItem to accept IEnumerable, not just two strings?

Order list of objects by a list of ids

So, I have a list of objects (let's say there are 20) and they have an id. Then I have another list (which is ordered correctly).
I had this linq to sort the object list by the id list:
var outcomeIds = outcomeRequestModels
.OrderByDescending(m => m.Score)
.Select(m => m.Id)
.ToList();
groupResponseModel.Outcomes = groupOutcomes
.OrderBy(m => outcomeIds.IndexOf(m.Id))
.ToList();
Now, this "would" work, but the problem is the outcomeIds only has a selection of ids in it. I would have thought that indexOf would return -1 for any id that was not found and it would be put under the matched ids. Instead they appear first in the list.
How can I modify my code to get the matching ids at the top and the rest at the bottom. I can't do a reverse, because it would mean that the order of the matching ids would be in reverse too.
Sounds like you want to order by the result of IndexOf, but to have the -1 values go to the end instead of the start. In that case, you could just process the value of the IndexOf to, say, int.MaxValue so it'll go at the end.
I've tidied up your code a bit to make it more readable - only the OrderBy is different to your original code.
var outcomeIds = outcomeRequestModels
.OrderByDescending(m => m.Score)
.Select(m => m.Id)
.ToList();
groupResponseModel.Outcomes = groupOutcomes
.Select(m => Tuple.Create(m, outcomeIds.IndexOf(m.Id))
.OrderBy(m => outcomeIds.IndexOf(m.Id) == -1 ? int.MaxValue : outcomeIds.IndexOf(m.Id))
.ToList();
Or, if you don't want to call IndexOf multiple times, you could extract the conditional statement into a method:
var outcomeIds = outcomeRequestModels
.OrderByDescending(m => m.Score)
.Select(m => m.Id)
.ToList();
groupResponseModel.Outcomes = groupOutcomes
.Select(m => Tuple.Create(m, outcomeIds.IndexOf(m.Id))
.OrderBy(m => orderByKeySelector(outcomeIds(m.Id)))
.ToList();
where orderByKeySelector is
private static int orderByKeySelector<T>(List<T> source, T value)
{
var indexOfValue = source.IndexOf(value);
return indexOfValue == -1 ? int.MaxValue : indexOfValue;
}
var outcomeIds = outcomeRequestModels
.OrderByDescending(m => m.Score)
.Select(m => m.Id)
.ToList();
groupResponseModel.Outcomes = groupOutcomes
.OrderBy(m => outcomeIds.IndexOf(m.Id) != -1
? outcomeIds.IndexOf(m.Id)
: outcomeIds.Max())
.ToList();
I prefer keeping it simple:
var outcomeList;
var unorderedList;
//check all elements of the ordered list in order
foreach(var item in orderedList)
{
//if your unordered list has this item
if(unorderedList.Any(item))
{
//add this item to the final list
outcomeList.Add(item);
//and remove it from unordered
unorderedList.Remove(item);
}
}
//at this point, you added all your matching entities in order, the rest is the remainder:
outcomeList.AddRange(unorderedList);
You can even turn this into an extension method for reusability.
Why not using mapping (say, id == 5 corresponds to 0, id = 123 to 1 etc.) with a help of dictionary? It will be efficient in case of long lists:
var order = outcomeRequestModels
.OrderByDescending(m => m.Score)
.Select((m, index) => new {
id = m.id,
index = index })
.ToDictionary(item => item.id, // id
item => item.index); // corresponding index
Now let's sort the 2nd list:
groupResponseModel.Outcomes = groupOutcomes
.OrderBy(m => order.TryGetValue(m.Id, out var order)
? order // if we have corresponding index, use it
: int.MaxValue) // otherwise, put the item at the bottom
.ToList();

Group and count Dictionary Items

I have a Dictionary<string, CachedImage> with which I'd like to group the items, count the number of items in the group and iterate through each group if the count is greater than 6500.
The CachedImage class contains Path And ExpiresUtc properties that I am interested in.
My Linq-fu is sadly lacking with complex queries so this is as far as I have got and I think I've already messed up. I'm assuming what I want is possible.
Any help would be appreciated, especially with a quick walkthrough.
Regex searchTerm = new Regex(#"(jpeg|png|bmp|gif)");
var groups= PersistantDictionary.Instance.ToList()
.GroupBy(x => searchTerm.Match(x.Value.Path))
.Select(y => new
{
Path = y.Key,
Expires = y.Select(z => z.Value.ExpiresUtc),
Count = y.Sum(z => z.Key.Count())
})
.AsEnumerable();
Try this:
var groups = PersistantDictionary.Instance.ToList()
.GroupBy(x => searchTerm.Match(x.Value.Path).Value)
.Where(g => g.Count() > 6500);
foreach (var group in groups)
{
Console.WriteLine("{0} images for extension {1}", group.Count(), group.Key);
foreach (KeyValuePair<string, CachedImage> pair in group)
{
//Do stuff with each CachedImage.
}
}
So to break this down:
PersistantDictionary.Instance.ToList()
Produces a list of KeyValuePair<string, CachedImage>.
.GroupBy(x => searchTerm.Match(x.Value.Path).Value)
Groups the list by the Regex match against the Path of the CachedImage. Note that I have used the Value property - the Match method returns a Match object, so it would be best to group by the actual text of the match. The outcome of this step will be an IEnumerable of <IGrouping<string, KeyValuePair<string, CachedImage>>>.
.Where(g => g.Count() > 6500);
This ensures that only those groups with > 6500 items will be retrieved.

Modifying an IEnumerable type

I have a a string IEnumerable type that I get from the below code.The var groups is an Enumerable type which has some string values. Say there are 4 values in groups and in the second position the value is just empty string "" .The question is how can I move it to the 4th ie the end position.I do not want to sort or change any order.Just move the empty "" value whereever it occurs to the last position.
List<Item> Items = somefunction();
var groups = Items.Select(g => g.Category).Distinct();
Simply order the results by their string value:
List<Item> Items = somefunction();
var groups = Items.Select(g => g.Category).Distinct().OrderByDescending(s => s);
Edit (following OP edit):
List<Item> Items = somefunction();
var groups = Items.Select(g => g.Category).Distinct();
groups = groups.Where(s => !String.IsNullOrEmpty(s))
.Concat(groups.Where(s => String.IsNullOrEmpty(s)));
You can't directly modify the IEnumerable<> instance, but you can create a new one:
var list = groups.Where(x => x != "").Concat(groups.Where(x => x == ""));
Note that in this query, groups is iterated twice. This is usually not a good practice for a deferred IEnumerable<>, so you should call ToList() after the Distinct() to eagerly evaluate your LINQ query:
var groups = Items.Select(g => g.Category).Distinct().ToList();
EDIT :
On second thought, there's a much easier way to do this:
var groups = Items.Select(g => g.Category).Distinct().OrderBy(x => x == "");
Note that this doesn't touch the order of the non-empty elements since OrderBy is stable.
var groups = Items.Select(g => g.Category).Distinct().OrderByDescending(s =>s);
I don't like my query but it should do the job. It selects all items which are not empty and unions it with the items which are empty.
var groups = Items.Select(g => g.Category).Distinct()
.Where(s => !string.IsNullOrEmpty(s))
.Union(Items.Select(g => g.Category).Distinct()
.Where(s => string.IsNullOrEmpty(s)));
Try something like
var temp = groups.Where(item => ! String.IsNullOrEmpty(item)).ToList<string>();
while (temp.Count < groups.Count) temp.Add("");

Categories