I have a detailed list and I want a new one with the elements of one property with no duplicates like this.
List<Class1> list = List<Class1>();
list.Add(new Class1("1", "Walmart", 13.54));
list.Add(new Class1("2", "Target", 12.54));
list.Add(new Class1("3", "Walmart", 14.54));
list.Add(new Class1("4", "BestBuy", 16.54));
list.Add(new Class1("5", "Walmart", 19.54));
list.Add(new Class1("6", "Amazon", 12.33));
My new list
List<Stores> newList = list.Select / FindAll / Group ?
I want this collection
newList = "Walmart", "Target", "BestBuy", "Amazon"
You need Distinct and Select.
var newList = list.Select(x => x.Name).Distinct().ToList();
If you also want your original class, you would have to get a bit more fancy.
Either get MoreLINQ and use its DistinctBy method:
var newList = list.DistinctBy(x => x.Name).ToList();
Or use a clever GroupBy hack:
var newList = list.GroupBy(x => x.Name).Select(x => x.First());
Lets assume that Class1 is defined as :
public class Class1
{
string Id {get;set;}
string Store {get;set;}
double Price {get;set;}
}
You can have your result as:
var result = list.Select(x => x.Store).Distinct().ToList();
You want to use Select() to select a specific property of the items:
list.Select(c => c.Company);
This returns an IEnumerable<string>.
You would then want to call .Distinct().
newList = list.Select(x => x.Store).Distinct().ToList();
Related
Check the code bellow. Here i am creating a method that simply should remove the duplicate from the list foo. If you see the list values they are product id and quantity derived by : so the first part of number before : is product and and second part of number after : is the product quantity. I am taking this list into RemoveDuplicateItems() method for processing. This method should remove all matching product id items from whole list but my current method just returns exactly same list which i am taking on input. How can i fix my method to remove those item from list which has matching first part number. (first part number means before :)
The final output on doo variable it should remove the first from from list which is 22:15 since it has matching with second one.
C#:
[HttpPost]
public JsonResult DoSomething()
{
var foo = new List<string>();
foo.Add("22:10");//this should removed by RemoveDuplicateItems() since it has `22` matching with second one
foo.Add("22:15");
foo.Add("25:30");
foo.Add("26:30");
var doo = RemoveDuplicateItems(foo);
return Json("done");
}
public List<string> RemoveDuplicateItems(List<string> AllItems)
{
var FinalList = new List<string>();
var onlyProductIds = new List<string>();
foreach (var item in AllItems)
{
Match result = Regex.Match(item, #"^.*?(?=:)");
onlyProductIds.Add(result.Value);
}
var unique_onlyProductIds = onlyProductIds.Distinct().ToList();
foreach (var item in AllItems)
{
Match result = Regex.Match(item, #"^.*?(?=:)");
var id = unique_onlyProductIds.Where(x => x.Contains(result.Value)).FirstOrDefault();
if (id != null)
{
FinalList.Add(item);
}
}
return FinalList;
}
Does this work for you?
List<string> doo =
foo
.Select(x => x.Split(':'))
.GroupBy(x => x[0], x => x[1])
.Select(x => $"{x.Key}:{x.Last()}")
.ToList();
There are multiple ways to achieve this, one is, as suggested by #Aluan Haddad is to use Linq. His comment uses the query syntax but would could use the method syntax too (I assumed you use C#8):
List<string> doo = foo.GroupBy(str => str[0..2])
.Select(entry => entry.Last())
.ToList();
Note that this works because the current implementation of GroupBy preserves ordering.
you can do it using Linq :
var doo = foo.Select(x =>
{
var split = x.Split(':');
return new { Key = split[0], Value = split[1] };
})
.GroupBy(x => x.Key)
.OrderBy(x => x.Key)
.Select(x =>
{
var max = x.LastOrDefault();
return $"{max.Key}:{max.Value}";
}
).ToList();
I have the following dataset, which is stored in a variable called list1.
countrypk | countryname | statepk | statename
---------------------------------------------
1 USA 1 New York
1 USA 2 California
2 Canada 3 Manitoba
I want to be able to group by countrypk, and retrieve the country name.
I have the following LINQ that achieves that effect, but was wondering if there was a better or more straight forward way to do it in LINQ.
var finalList = list1
.GroupBy(item => item.countrypk)
.Where(item => item.Count() > 0)
.Select(item => item.First())
The desired output is:
countrypk | countryname
---------------------------------------------
1 USA
2 Canada
The addition of the Where is not needed. If you have a group it contains at least a single item in it. You can do something like this:
list1.GroupBy(item => item.countrypk)
.Select(item => new { item.Key, item.First().countryname} );
Or using a different overload of GroupBy:
list1.GroupBy(item => item.countrypk,
selector => new { selector.countrypk, selector.countryname} )
.Select(group => group.First())
If you grouped by countrypk, your result set wouldn't have duplicates in it. Your desired result set has duplicate countrypk values in it (1). To get your desired result set, do this:
var finalList = list1.Select(s => new { s.countrypk, s.countryname });
Edit: nevermind this part above, OP editted the question.
I want to be able to group by countrypk, and retrieve the country name What you're asking for is different than what your result set shows. If you want a map of countrypk to country name using your list1, here's one way to do it:
var finalList = list1
.GroupBy(g => new { g.countrypk, g.countryname })
.ToDictionary(k => k.Key.countrypk, v => v.Key.countryname);
Note that you don't need GroupBy to do this. Here's another solution:
var finalList = list1
.Select(s => new { s.countrypk, s.countryname })
.Distinct()
.ToDictionary(k => k.countrypk, v => v.countryname);
In either case, to get the country name for id 1, do this:
var countryName = finalList[1];
Try the following
var finalList = list1
.GroupBy(item => item.countrypk)
.Select(g => new { countrypk = g.Key, countryname = g.First().countryname });
which should provided the desired output
Basically you want to remove duplicates by countrypk and select only first two columns? Use this extension:
public static IEnumerable<TSource> DistinctBy<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector)
{
HashSet<TResult> set = new HashSet<TResult>();
foreach(var item in source)
{
var selectedValue = selector(item);
if (set.Add(selectedValue))
yield return item;
}
}
And then
var finalList = list1
.DistinctBy(item => item.countrypk)
.Select(item=> new {item.countrypk, item.countryname })
.ToList();
I have list of classes and how to split, group them?
class CLA
{
string GroupName;
double Class;
double Value;
}
...
public List<List<CLA>> Dividr (List<CLA> a)
{
List<List<CLA> Clist = new List<List<CLA>>();
Clist.Addrange(...) //Here
returnn Clist;
}
As for dividing, it would be split by it's properties, GroupName, Class.
Example, if elements have same GroupName and Class it will be one List<>.
Simply use GroupBy. Then, as you want an inner list and not IEnumerable use ToList() for each group:
List<CLA> data = new List<CLA>();
var result = data.GroupBy(item => new { item.GroupName, item.Class })
.Select(group => group.ToList()).ToList();
Unless for specific reasons consider returning IEnumerable<IEnumerable<CLA>> instead - shame to already execute the query if not yet needed:
var result = data.GroupBy(item => new { item.GroupName, item.Class })
.Select(group => group);
I have an object that has a list of another object in it. i.e Object1 contains List<Object2>.
Assuming this is the definition of object 2:
public class Object2
{
string code,
string name,
decimal amount
}
I want to be a able to make a list2 from the list whose value will contain what something similar to what a select name, code, sum(amount) group by code kinda statement could have given me
this is what i did but it didnt contain what i needed on passing through.
var newlist = obj2List.GroupBy(x => x.code)
.Select(g => new { Amount = g.Sum(x => x.amount) });
I want code and name in the new list just like the sql statement above.
You're almost there:
var newlist = obj2List.GroupBy(x => x.code)
.Select(g => new
{
Code = g.First().code,
Name = g.First().name,
Amount = g.Sum(x => x.amount)
});
This groups the items by code and creates an anonymous object for each group, taking the code and name of first item of the group. (I assume that all items with the same code also have the same name.)
If you are grouping by code and not by name you'd have to choose something for name from the list, perhaps with First() or Last() or something.
var newlist = obj2List.GroupBy(x => x.code).Select(g => new {
Code = g.Key,
Name = g.First().name,
Amount = g.Sum(x => x.amount)
});
var query = Object1.Obj2List
.GroupBy(obj2 => obj2.code)
.Select(g => new {
Names = string.Join(",", g.Select(obj2.name)),
Code = g.Key,
Amount = g.Sum(obj2 => obj2.Amount)
});
Since you group by code only you need to aggregate the name also in some way. I have used string.Join to create a string like "Name1,Name2,Name3" for each code-group.
Now you could consume the query for example with a foreach:
foreach(var x in query)
{
Console.WriteLine("Code: {0} Names: {1} Amount: {2}"
, x.Code, x.Names, x.Amount);
}
Instead of using the LINQ Extension Methods .GroupBy() and .Select() you could also use a pure LINQ statement which is way easier to read if you come from a SQL Background.
var ls = new List<Object2>();
var newLs = from obj in ls
group obj by obj.code into codeGroup
select new { code = codeGroup.Key, amount = codeGroup.Sum(s => s.amount) };
I am trying to determine if there is a better way to execute the following query:
I have a List of Pair objects.
A Pair is defined as
public class Pair
{
public int IDA;
public int IDB;
public double Stability;
}
I would like to extract a list of all distinct ID's (ints) contained in the List<Pair>.
I am currently using
var pIndices = pairs.SelectMany(p => new List<int>() { p.IDA, p.IDB }).Distinct().ToList();
Which works, but it seems unintuitive to me to create a new List<int> only to have it flattened out by SelectMany.
This is another option I find unelegant to say the least:
var pIndices = pairs.Select(p => p.IDA).ToList();
pIndices.AddRange(pairs.Select((p => p.IDB).ToList());
pIndices = pIndices.Distinct().ToList();
Is there a better way? And if not, which would you prefer?
You could use Union() to get both the A's and B's after selecting them individually.
var pIndices = pairs.Select(p => p.IDA).Union(pairs.Select(p => p.IDB));
You could possibly shorten the inner expression to p => new [] { p.IDA, p.IDB }.
If you don't want to create a 2-element array/list for each Pair, and don't want to iterate your pairs list twice, you could just do it by hand:
HashSet<int> distinctIDs = new HashSet<int>();
foreach (var pair in pairs)
{
distinctIDs.Add(pair.IDA);
distinctIDs.Add(pair.IDB);
}
This is one without a new collection:
var pIndices = pairs.Select(p => p.IDA)
.Concat(pairs.Select(p => p.IDB))
.Distinct();
Shorten it like this:
var pIndices = pairs.SelectMany(p => new[] { p.IDA, p.IDB }).Distinct().ToList();
Using Enumerable.Repeat is a little unorthodox, but here it is anyway:
var pIndices = pairs
.SelectMany(
p => Enumerable.Repeat(p.IDA, 1).Concat(Enumerable.Repeat(p.IDB, 1))
).Distinct()
.ToList();
Finally, if you do not mind a little helper class, you can do this:
public static class EnumerableHelper {
// usage: EnumerableHelper.AsEnumerable(obj1, obj2);
public static IEnumerable<T> AsEnumerable<T>(params T[] items) {
return items;
}
}
Now you can do this:
var pIndices = pairs
.SelectMany(p => EnumerableHelper.AsEnumerable(p.IDA, p.IDB))
.Distinct()
.ToList();