Split into KeyValuePair instead of Array - linq

I have a string of data:
Key1=Value1,Key2=Value2,KeyN=ValueN
I'm trying to split the string into
List<KeyValuePair<string, string>>
I can easily do this:
List<string[]> values = item.Split( ',' ).Select( p => p.Split( '=' ) ).ToList();
but I just can't figure out the way to get that into the List of KeyValuePair's. The closest I've gotten so far is:
List<KeyValuePair<string, string>> values = item.Split( ',' )
.Select( p => new KeyValuePair<string, string>(){ p.Split( '=' ) } ).ToList();
But that's still a bit off :(
I know I can easily do it with a loop but I'd really like to get it working in Linq as practice makes perfect. I've seen quite a few examples already of similar questions like this one, but I can't seem to join the dots between those questions and mine so please forgive me if I've accidentally posted a duplicate.
Any help would really be appreciated, thanks :)

What you've done so far is good. Then, you have two ways to achieve what you want:
Create a method ToKeyValuePair
public static KeyValuePair<string, string> ToKeyValuePair(string[] array)
{
if (array.Length != 2)
throw new ArgumentException("The array must contain exactly 2 elements.");
return new KeyValuePair<string, string>(array[0], array[1]);
}
var values = (item.Split( ',' )
.Select( p => ToKeyValuePair(p.Split( '=' ))))
.ToList();
Use the LINQ query syntax
If I convert the above line into query syntax:
var values = (from p in item.Split( ',' )
select ToKeyValuePair(p.Split( '=' )))
.ToList();
Not much has changed.
But, thanks to this new syntax, it is quite easy to remove the usage of ToKeyValuePair(...) thanks to the let clause:
var values = (from p in item.Split( ',' )
let splittedP = p.Split( '=' ) // Declares a variable
select new KeyValuePair<string, string>(splittedP[0], splittedP[1]))
.ToList();
Of course, the last line can be written with Extention methods syntax (ie with .Select(p=>...)), but is hard to read:
var values = (item.Split(',')
.Select(p => new { p, splittedP = p.Split('=') })
.Select(p => new KeyValuePair<string, string>(p.splittedP[0], p.splittedP[1])))
.ToList();

I know this is an old question but I stumbled across it on google. I solved the problem using the accepted answer but a shortened it a little. You don't need the new { p, splittedP = p.Split('=') } part, just p.Split('=')
var values = data.Split(',').Select(p=>p.Split('='))
.Select(s => new KeyValuePair<string, string>(s[0], s[1]))
.ToList();
You could also do something like this if your keys are unique:
var values = data.Split(',').Select(p => p.Split('='))
.ToDictionary(k => k[0], v => v[1]);
Which is much shorter and basically gets you a list with with O(1) access.
(This is with .NET 4.5)

use above code. must trim the key.
public static string GetUserInfo(this X509Certificate2 x509,X509Oid oid)
{
try
{
var kvs = x509.Subject.Split(',').Select(x => new KeyValuePair<string, string>(x.Split('=')[0].Trim(), x.Split('=')[1].Trim())).ToList();
string value = kvs.FirstOrDefault(A => A.Key == oid.ToString()).Value;
return value;
}
catch (Exception)
{
}
return "";
}
Add an enum file.
public enum X509Oid
{
O,
E,
L,
C,
S,
G,
SN,
CN,
Street
}

Related

Except keyword issue in LINQ

I want to capture the column names which needs to be added to the SQL table in order to insert the data.
Columnspresent-- List of all columns in the file ("Node", "Logtime", "Reason","ID","Comments")
existingtablecolumnsPresent -- List of all columns in the existing table in SQL("Node","Value","Reason","ID","Comments","logtime")
columnsNotPresent -- List of columns that needs to be added to the SQL table ( have to get "Value" in the output but not getting)..
List<string> columnsPresent =
dt.Columns.Cast<DataColumn>()
.Select(a => a.ColumnName.ToLower())
.ToList();
List<string> existingtablecolumnsPresent =
existingtable.Columns.Cast<DataColumn>()
.Select(a => "[" + a.ColumnName.ToLower() + "]")
.ToList();
List<string> columnsNotPresent =
columnsPresent.OrderBy(t => t)
.Except(existingtablecolumnsPresent.OrderBy(t => t))
.ToList();
The above code is not giving the correct results if there is change in order of columns .Please advise.
you may try this (it needn't order by..)
List<string> existingtablecolumnsPresentNoSqrBr = new List<string>();
existingtablecolumnsPresent.ForEach(c => {
c = c.Replace("[", string.Empty);
c = c.Replace("]",string.Empty);
existingtablecolumnsPresentNoSqrBr.Add(c);
});
List<string> columnsNotPresent =
columnsPresent.Except(existingtablecolumnsPresentNoSqrBr)
.ToList();
really, if you avoid to .Select(a => "[" + a.ColumnName.ToLower() + "]") you can use the second query directly on existingtablecolumnsPresent..

LINQ read Select values

I have a LINQ query where I want to select and read the p.Api value.
var api = DataAccessNew.Instance.dcServers.Where(p => p.Ip == IpAddress).Select(p => p.Api);
How do I read the p.Api value?
I have tried api.ToString() but I get SQL instead of actual column value.
You are getting an IEnumerable<> back (and your ToString call is showing you the value of that expression).
If you are expecting a single value, do this:
var api = DataAccessNew.Instance.dcServers
.Where(p => p.Ip == IpAddress)
.Select(p => p.Api)
.Single();
You might be interested to read about the other methods like Single(): SingleOrDefault, First, FirstOrDefault. Which one you used depends on whether you are expecting a single or multiple values returned (Single vs. First) and what you want to happen if there are no values (the *Default methods will return the type default instead of throwing an exception).
Or if you want to look at all the returned values:
var api = DataAccessNew.Instance.dcServers
.Where(p => p.Ip == IpAddress)
.Select(p => p.Api);
foreach (var apiValue in api)
{
// apiValue will have the value you're looking for.
}
Try this snippet of code:
string apiValue = api.FirstOrDefault().ToString();
your syntex seems ok..
By the way try this
string api =DataAccessNew.Instance.dcServers.Where(p => p.Ip == IpAddress).Select(p => p.Api).FirstOrDefault();
if p.Ip is a unique key in your table you could try to add .FirstOrDefault() after your Linq query.
public string getselectedvalue(ListBox l)
{
string vtext="",vval="";
var selectedQueryText = l.Items.Cast<ListItem>().Where(item => item.Selected);
var selectedQueryVal = l.Items.Cast<ListItem>().Where(item => item.Selected).Select(item => item.Value);
vtext= String.Join("','", selectedQueryText ).TrimEnd();
vval= String.Join("','", selectedQueryVal ).TrimEnd();
return v;
}

Better way to check resultset from LINQ projection than List.Count?

Is there a better way to check if a LINQ projection query returns results:
IList<T> TList = db.Ts.Where(x => x.TId == 1).ToList(); // More canonical way for this?
if (TitleList.Count > 0)
{
// Result returned non-zero list!
string s = TList.Name;
}
You can use Any(), or perhaps more appropriately to your example, SingleOrDefault(). Note that if you are expecting more than one result and plan to use all of them, then it doesn't really save anything to use Any() instead of converting to a List and checking the length. If you don't plan to use all the results or you're building a larger query that might change how the query is performed then it can be a reasonable alternative.
var item = db.Ts.SingleOrDefault( x => x.TId == 1 );
if (item != null)
{
string s = item.Name;
...
}
or
var query = db.Ts.Where( x => x.Prop == "foo" );
if (query.Any())
{
var moreComplexQuery = query.Join( db.Xs, t => t.TId, x => x.TId );
...
}

LINQ Group By into a Dictionary Object

I am trying to use LINQ to create a Dictionary<string, List<CustomObject>> from a List<CustomObject>. I can get this to work using "var", but I don't want to use anonymous types. Here is what I have
var x = (from CustomObject o in ListOfCustomObjects
group o by o.PropertyName into t
select t.ToList());
I have also tried using Cast<>() from the LINQ library once I have x, but I get compile problems to the effect of it being an invalid cast.
Dictionary<string, List<CustomObject>> myDictionary = ListOfCustomObjects
.GroupBy(o => o.PropertyName)
.ToDictionary(g => g.Key, g => g.ToList());
I cannot comment on #Michael Blackburn, but I guess you got the downvote because the GroupBy is not necessary in this case.
Use it like:
var lookupOfCustomObjects = listOfCustomObjects.ToLookup(o=>o.PropertyName);
var listWithAllCustomObjectsWithPropertyName = lookupOfCustomObjects[propertyName]
Additionally, I've seen this perform way better than when using GroupBy().ToDictionary().
For #atari2600, this is what the answer would look like using ToLookup in lambda syntax:
var x = listOfCustomObjects
.GroupBy(o => o.PropertyName)
.ToLookup(customObject => customObject);
Basically, it takes the IGrouping and materializes it for you into a dictionary of lists, with the values of PropertyName as the key.
This might help you if you to Get a Count of words. if you want a key and a list of items just modify the code to have the value be group.ToList()
var s1 = "the best italian resturant enjoy the best pasta";
var D1Count = s1.ToLower().Split(' ').GroupBy(e => e).Select(group => new { key = group.Key, value = group.Count() }).ToDictionary(e => e.key, z => z.value);
//show the results
Console.WriteLine(D1Count["the"]);
foreach (var item in D1Count)
{
Console.WriteLine(item.Key +" "+ item.Value);
}
The following worked for me.
var temp = ctx.Set<DbTable>()
.GroupBy(g => new { g.id })
.ToDictionary(d => d.Key.id);

Linq query for data aggregation

I have this class
public class Line
{
public string ConnectionsIndex{get;set;}
}
my Linq problem is that I have to aggregate these Lines
var l1 = new Line{ ConnectionsIndex="01,02"};
var l2 = new Line{ ConnectionsIndex="02,03"};
var l3 = new Line{ ConnectionsIndex="01,03"};
into this
var l4 = new Line{ ConnectionsIndex="01,02,03"};
It's possible to do with Linq?
DETAIL:
The thing is more complicate (at least for me) when I add the other items that I have in my collection.
var l5 = new Line (ConnectionsIndex = "02,04");
var l6 = new Line (ConnectionsIndex = "03,06");
because do not exist other lines with the pairs 03,04 , 01,04 , 01,06 and 02,06
I do not know if I have explained it well ...
in practice, imagine you have all the points of a polygon, I want to get a line of all the items from the query by giving a list of connections between all points of each polygon.
(my list contains more than one polygon)
One point should not be included in result if not connected to all others.
This is an example of my list content:
ConnectionsIndex="166,171"
ConnectionsIndex="166,174"
ConnectionsIndex="166,333"
ConnectionsIndex="169,170"
ConnectionsIndex="171,175"
ConnectionsIndex="171,334"
ConnectionsIndex="167,174"
ConnectionsIndex="172,174"
ConnectionsIndex="174,335"
ConnectionsIndex="177,341"
ConnectionsIndex="180,200"
ConnectionsIndex="181,183"
ConnectionsIndex="182,199"
ConnectionsIndex="184,185"
ConnectionsIndex="186,188"
ConnectionsIndex="189,192"
ConnectionsIndex="190,230"
ConnectionsIndex="191,375"
In this List you have for example a triangle between 166, 171 and 334
More detail:
var group = lines.Where(x => x.ConnectionsIndex.Split(',').Contains(line. ConnectionsIndex.Split(',')[0]) ||
x. ConnectionsIndex.Split(',').Contains(line. ConnectionsIndex.Split(',')[1])).ToList();
if (group.Count()==1)
{
straight_lines.Add(line);
}
else
{
//Here I have a "group" with all the lines between point.. I want to get distinc points
}
Something like:
var connections = (from line in lines
from connection in line.Split(',')
select connection).Distinct()
.ToArray();
Line line = new Line { ConnectionsIndex = string.Join(",", connections) };
This doesn't order the connections, but you can easily add that if you need it.
This would all be cleaner if you were happy to have ConnectionsIndex as a collection of strings instead of a single delimited string, of course :)
I used this:
var l4 = new Line{
ConnectionsIndex =
string.Join(",", (lines.SelectMany(x => x.ConnectionsIndex.Split(','))
.Distinct()
.OrderBy(s => s)).ToArray())
};
This is the bad way I have found... and it works!
var l = linee.Distinct(
(a, b) => a.ConnectionsIndex == b.ConnectionsIndex,x=>x.ConnectionsIndex.GetHashCode())
.ToList();
var single_lines = new List<Linea>();
var multiple_lines = new List<Linea>();
foreach (var linea in l)
{
var group = l
.Where(x => x.ConnectionsIndex.Split(',').Contains(linea.ConnectionsIndex.Split(',')[0]) ||
x.ConnectionsIndex.Split(',').Contains(linea.ConnectionsIndex.Split(',')[1])).ToList();
if (group.Count()==1)
{
single_lines.Add(linea);
}
else
{
var indexes = new List<string>();
var dist = group.Select(x => new {Index = x.ConnectionsIndex.Split(',').ToList()}).ToList();
foreach (var linea1 in dist)
{
indexes=indexes.Concat(linea1.Index).ToList();
}
var indexstring = new StringBuilder();
foreach (var s in indexes.Distinct().OrderBy(x=>Convert.ToInt32(x)))
{
indexstring.Append(s).Append(',');
}
indexstring.Remove(indexstring.Length - 1, 1);
multiple_lines.Add(new Linea() {ConnectionsIndex = indexstring.ToString()});
}
}
var multi_distinct=multiple_lines.Distinct(
(a, b) => a.ConnectionsIndex == b.ConnectionsIndex, x => x.ConnectionsIndex.GetHashCode())
.ToList();
linee = single_lines.Concat(multi_distinct).ToList();
If you find or known better solutions, you are welcome!
Just a note that I think what you're asking for is a way to find maximal cliques (a concept from graph theory). This is known to be an NP-Hard problem. I think your version will work sometimes, and hopeflly for those cases you're interested in. But, not for complicated cases where anything may be connected to anything else. Indeed, if you have a lot of nodes, those cases aren't feasible, even with large CPU cycle budgets (regardless of LINQ).

Resources