LINQ - Self join in list - c#

I have a List defined as below
var MList = new List<KeyValuePair<String, Object>>();
(Example list item { "Prj1" , {e1,e2,e3} }
Ideally, a List item would contain a Project Name (string) and an array of admin ids (Object). I need to create and populate a new List with items which are expanded from the original list.
So the resulting list would look like below, with each item being a string.
{"Prj1","e1"}
{"Prj1","e2"}
{"Prj1","e3"}
How can i use LINQ to fetch items into the new list in the above format

This can be done using SelectMany, like this:
var expanded = MList.SelectMany(
item => ((IEnumerable<string>)item.Value).Select( str =>
new KeyValuePair<string,object>(item.Key, str)
)
);
The above assumes that Object contains an IEnumerable<string>.

Is your Object an IEnumerable?
var newlist = from x in mList
from v in (IEnumerable<object>)x.Value
select new
{
x.Key,
v,
};

Related

How to modify each element of a list into a new list?

I would like to learn how to do the following code using a => or ForEach or something else. It basically gets all elements of List1, modifies it, and then stores it into a new List2. List1 is not altered.
//List1 exists from before
List<string> List2 = new List<string>();
foreach (element in List1)
{
List2.Add(element + " concact string");
}
Is there an easy way to write this code in a more concise/readable way?
Using the System.Linq extension method Select, it's simple to select each item from List1, modify it, and return the results as a new List<string>:
List<string> List2 = List1.Select(element => element + " concat string").ToList();
var newList = List1.Select(x => x.Concat(" concact string")).ToList();

How to group a list with Linq

I have a list which I get from a database. The structure looks like (which I'm representing with JSON as it's easier for me to visualise)
{id:1
value:"a"
},
{id:1
value:"b"
},
{id:1
value:"c"
},
{id:2
value:"t"
}
As you can see, I have 2 unique ID's, ID 1 and 2. I want to group by the ID. The end result I'd like is
{id:1,
values:["a","b","c"],
},
{id:2,
values["g"]
}
Is this possible with Linq? At the moment, I have a massive complex foreach, which first sorts the list (by ID) and then detects if it's already been added etc but this monstrous loop made me realise I'm doing wrong and honestly, it's too embarrassing to share.
You can group by the item Id and have the resulting type be a Dictionary<int, List<string>>
var result = myList.GroupBy(item => item.Id)
.ToDictionary(item => item.Key,
item => item.Select(i => i.Value).ToList());
You can either use GroupBy method on IEnumerable to create IGrouping object that contains a key and grouped objects or you can use ToLookupto create exactly what you want in result:
yourList.ToLookup(m => m.id, m => m.value);
This creates a hashed collection of keys with their values.
For more information please see below post:
https://www.c-sharpcorner.com/UploadFile/d3e4b1/practical-usage-of-using-tolookup-method-in-linq-C-Sharp/
Just a little more detail to emphasize the difference between the ToLookup approach and the GroupBy approach:
// class definition
public class Item
{
public long Id { get; set; }
public string Value { get; set; }
}
// create your list
var items = new List<Item>
{
new Item{Id = 0, Value = "value0a"},
new Item{Id = 0, Value = "value0b"},
new Item{Id = 1, Value = "value1"}
};
// this approach results in a List<string> (a collection of the values)
var lookup = items.ToLookup(i => i.Id, i => i.Value);
var groupOfValues = lookup[0].ToList();
// this approach results in a List<Item> (a collection of the objects)
var itemsGroupedById = items.GroupBy(i => i.Id).ToList();
var groupOfItems = itemsGroupedById[0].ToList();
So, if you want to work with values only after grouping, then you could take the first approach; if you want to work with objects after grouping, you could take the second approach. And, these are just a couple example implementations, there are plenty of ways to accomplish your goal.
First convert to a Lookup then select into a list, like so:
var groups = list
.ToLookup
(
item => item.ID,
item => item.Value
)
.Select
(
item => new
{
ID = item.Key,
Values = item.ToList()
}
)
.ToList();
The resulting JSON looks like this:
[{"ID":1,"Values":["a","b","c"]},{"ID":2,"Values":["t"]}]
Link to working example on DotNetFiddle.

Filter Object List by Property which is a integer list

I have a class like below
Class MyClass {
string Name ;
List<int> scoreList; //this contains integer values
}
I have a code to fill object list of above class type from database
List<MyClass> newList = //fill from db
I have a separate integer list having some numbers
List<int> filterList = new filterList();
Now I want to filter object list (newList) as any value(s) of property scoreList in filterList values
I wrote code like below but it seems to give incorrect results
newList = newList .Where(x => x.scoreList.All(s => filterList .Contains(s))).ToList();
Can you help me to write a Linq query using c# to do above ?
If you want all items from newList where one or more of the scoreList values appears in filterList, then this should do it:
newList = newList.Where(x => x.scoreList.Any(s => filterList.Contains(s))).ToList();
newList = newList.Where(s=>s.ScoreList.Intersect(filterList).Any()).ToList();

Linq query change from List<string> to List<T> where clause

I have a linq query that works when it I had a list of a single value now that I change to having a List that has several properties I need to change the where clause
So this works:
List<string> etchList = new List<string>();
etchList.Add("24");
var etchVect = (from vio in AddPlas
where etchList.Any(v => vio.Key.Formatted.Equals(v))
let firstOrDefault = vio.Shapes.FirstOrDefault()
where firstOrDefault != null
select new
{
EtchVectors = firstOrDefault.Formatted
}).ToList();
However I have a new hard coded list (which will represent incoming data:
List<ExcelViolations> excelViolations = new List<ExcelViolations>();
excelViolations.Add(new ExcelViolations
{
VioID = 24,
RuleType = "SPACING",
VioType = "Line-Line",
XCoordinate = 6132,
YCoordinate = 10031.46
});
So the NEW Linq query looks like this, but is obviously will not work as
AddPlas is a List and so using this other list of excelviolations, I wish to have it do where on each one of the properties in the excelviolations list
var etchVect = (from vio in AddPlas
where excelViolations.Any(vioId => vio.Key.Formatted.Equals(vioId))
let firstOrDefault = vio.Shapes.FirstOrDefault()
select new
{
EtchVectors = firstOrDefault.Formatted
}).ToList();
Now, since this is a list within a list, I would like to do something like add in each of the properties
so for example:
where excelViolations.VioID.Any(vioId => vio.Key.Formatted.Equals(vioId))
However that is not possible, but you see that I'm trying to access the property of VioID that is in the excelViolations and match it to the Key which is in vio list
Just change this line
where excelViolations.Any(vioId => vio.Key.Formatted.Equals(vioId))
to
where excelViolations.Any(excelVio => vio.Key.Formatted.Equals(excelVio.VioID))
then i thought it will works

Generate a map of list element indices using Linq

I want to take a List, and generate a Dictionary which maps each element to its index in the List. I can do this like so, for a List<string>:
var myList = new List<string>{ /* populate list */ };
var orderMap = new Dictionary<string, int>();
foreach (var element in myList)
{
orderMap[element] = myList.IndexOf(element);
}
Basically, I want to take a list like:
Apple
Banana
Orange
And return a map showing indices:
Apple -> 0
Banana -> 1
Orange -> 2
How can I do this with Linq? I think something like this should work:
orderMap = myList.Select( x => /* return a key value pair mapping x to myList.IndexOf(x) */ );
But I can't figure out the right syntax for it. Besides, can you refer to the list itself in the delegate used for Select?
While you can refer to the list within the delegate, it's not generally a good idea. You really want to use the overload of Select which provides the index as well as the value:
var dictionary = list.Select((value, index) => new { value, index })
.ToDictionary(p => p.value, p => p.index);
Note that this will throw an exception if you have any duplicate elements.
You could try the ToDictionary extension method:
int index = 0;
orderMap = myList.ToDictionary(x => x, x => index++);
Take a look at this overload of ToDictionary<TKey, TValue>(). It takes to functions to convert the input element into a Key and a Value.
e.g.
var myList = new List<string>{ /* populate list */ };
var orderMap = myList.ToDictionary(x => myList.IndexOf(x), x => x);
However, one problem with this is if the elements of myList aren't unique.

Categories