How could I convert these foreach loops into a LINQ-expression? - c#

I used ReSharper to inspect the code issues in my project and it notified me that the following loop could be converted into a LINQ-expression:
var dictionary = new Dictionary<string, string[]>
{
{ "400", new[] { "12345", "54321", "51423" } },
{ "500", new[] { "67890", "09876", "63727" } },
{ "600", new[] { "41713", "98234", "96547" } },
{ "700", new[] { "00000", "67990", "83752" } }
};
// ...
var targetValue = "41713";
foreach (string group in dictionary.Keys)
{
foreach (string name in dictionary[group])
{
if (name == targetValue)
return group;
}
}
return "User";
The loop basically checks the dictionary's values (string arrays) to see if targetValue belongs to any of them and returns the key of that array if found inside.
I tried doing the following, but clearly it just returns the value inside if its value is equivalent to targetValue.
var r = dictionary
.SelectMany(t => t.Value)
.FirstOrDefault(t => t == targetValue);

So you want to get the first key in the dictionary which string[]-value contains a given value?
var pairs = dictionary.Where(kv => kv.Value.Contains(myValue));
if (pairs.Any())
{
string group = pairs.First().Key;
}
or less readable but a little bit more efficient since it executes the query only once:
var pair = dictionary.FirstOrDefault(kv => kv.Value.Contains(myValue));
if (!pair.Equals(default(KeyValuePair<string, string[]>)))
{
string group = pair.Key;
}
last but not least another approach which is my favorite and also uses the "User"-default:
string group = dictionary.Where(kv => kv.Value.Contains(myValue))
.Select(kv=> kv.Key)
.DefaultIfEmpty("User")
.First();

var r = dictionary.FirstOrDefault(
x => x.Value.FirstOrDefault(y => y == myValue) != null);

This will also get the desired value back or null if it does not exist:
EDIT:
var result = dictionary.SkipWhile(n => !n.Value.Contains(myValue)).FirstOrDefault().Key;
//another way to get the key
//var result = dictionary.SingleOrDefault(n => n.Value.Contains(myValue)).Key;
if (result != null)
{
//do whatever with the result variable here
}

Related

How can I convert IList<object> to string array?

How can I convert IList objects to string array?
While working on the telegram bot, some difficulties arose. I get a IList<object> from google table with some information. I need to convert this IList<object> to an array of strings. How can I do this?
static void ReadBudgetTypes()
{
var range = $"{settingsSheet}!B3:B";
var request = service.Spreadsheets.Values.Get(SpreadsheetId, range);
var response = request.Execute();
var values = response.Values; // here i get list of objects from google table
if (values != null && values.Count > 0)
{
foreach (var row in values)
{
Console.WriteLine("{0}", row[0]);
}
}
else
{
Console.WriteLine("No data!");
}
}
Assuming cells may not be strings and may (or may not) have null values, you can print for each cell of the row:
// assumes ToString() gives a meaningful string
var listOfStrings = row.Select(x => x?.ToString()).ToList();
foreach(string cell in listOfStrings)
Console.WriteLine(cell);
or the whole row, joined by a separator
Console.WriteLine(string.Join(", ", row);
If you know the cells are strings you can just cast
var listOfStrings = row.Cast<string>().ToList();
// or
var listOfStrings = row.Select(x => (string)x).ToList();
and then repeat either of the above (loop or string.Join).
If items could be null,
var listOfStrings = row.Select(x => (x ?? (object)"").ToString()).ToList();
you can try this:
var tempList=List<string>();
string[] arrayList=null;
if (values != null && values.Count > 0)
{
foreach (var row in values)
{
tempList.Add(row[0]);
}
arrayList=tempList.ToArray();
}
Try something like this:
IList<object> list = new List<object>(){ "something", "something else" };
string[] array = list.Select(item => (String)item).ToArray();

C# Use Regex to split on Words

This is a stripped down version of code I am working on. The purpose of the code is to take a string of information, break it down, and parse it into key value pairs.
Using the info in the example below, a string might look like:
"DIVIDE = KE48 CLACOS = 4556D DIV = 3466 INT = 4567"
One further point about the above example, at least three of the features we have to parse out will occasionally include additional values. Here is an updated fake example string.
"DIVIDE = KE48, KE49, KE50 CLACOS = 4566D DIV = 3466 INT = 4567 & 4568"
The problem with this is that the code refuses to split out DIVIDE and DIV information separately. Instead, it keeps splitting at DIV and then assigning the rest of the information as the value.
Is there a way to tell my code that DIVIDE and DIV need to be parsed out as two separate values, and to not turn DIVIDE into DIV?
public List<string> FeatureFilterStrings
{
// All possible feature types from the EWSD switch.
get
{
return new List<string>() { "DIVIDE", "DIV", "CLACOS", "INT"};
}
}
public void Parse(string input){
Func<string, bool> queryFilter = delegate(string line) { return FeatureFilterStrings.Any(s => line.Contains(s)); };
Regex regex = new Regex(#"(?=\\bDIVIDE|DIV|CLACOS|INT)");
string[] ms = regex.Split(updatedInput);
List<string> queryLines = new List<string>();
// takes the parsed out data and assigns it to the queryLines List<string>
foreach (string m in ms)
{
queryLines.Add(m);
}
var features = queryLines.Where(queryFilter);
foreach (string feature in features)
{
foreach (Match m in Regex.Matches(workLine, valueExpression))
{
string key = m.Groups["key"].Value.Trim();
string value = String.Empty;
value = Regex.Replace(m.Groups["value"].Value.Trim(), #"s", String.Empty);
AddKeyValue(key, value);
}
}
private void AddKeyValue(string key, string value)
{
try
{
// Check if key already exists. If it does, remove the key and add the new key with updated value.
// Value information appends to what is already there so no data is lost.
if (this.ContainsKey(key))
{
this.Remove(key);
this.Add(key, value.Split('&'));
}
else
{
this.Add(key, value.Split('&'));
}
}
catch (ArgumentException)
{
// Already added to the dictionary.
}
}
}
Further information, the string information does not have a set number of spaces between each key/value, each string may not include all of the values, and the features aren't always in the same order. Welcome to parsing old telephone switch information.
I would create a dictionary from your input string
string input = "DIVIDE = KE48 CLACOS = 4556D DIV = 3466 INT = 4567";
var dict = Regex.Matches(input, #"(\w+?) = (.+?)( |$)").Cast<Match>()
.ToDictionary(m => m.Groups[1].Value, m => m.Groups[2].Value);
Test the code:
foreach(var kv in dict)
{
Console.WriteLine(kv.Key + "=" + kv.Value);
}
This might be a simple alternative for you.
Try this code:
var input = "DIVIDE = KE48 CLACOS = 4556D DIV = 3466 INT = 4567";
var parts = input.Split(new [] { '=', ' ' }, StringSplitOptions.RemoveEmptyEntries);
var dictionary =
parts.Select((x, n) => new { x, n })
.GroupBy(xn => xn.n / 2, xn => xn.x)
.Select(xs => xs.ToArray())
.ToDictionary(xs => xs[0], xs => xs[1]);
I then get the following dictionary:
Based on your updated input, things get more complicated, but this works:
var input = "DIVIDE = KE48, KE49, KE50 CLACOS = 4566D DIV = 3466 INT = 4567 & 4568";
Func<string, char, string> tighten =
(i, c) => String.Join(c.ToString(), i.Split(c).Select(x => x.Trim()));
var parts =
tighten(tighten(input, '&'), ',')
.Split(new[] { '=', ' ' }, StringSplitOptions.RemoveEmptyEntries);
var dictionary =
parts
.Select((x, n) => new { x, n })
.GroupBy(xn => xn.n / 2, xn => xn.x)
.Select(xs => xs.ToArray())
.ToDictionary(
xs => xs[0],
xs => xs
.Skip(1)
.SelectMany(x => x.Split(','))
.SelectMany(x => x.Split('&'))
.ToArray());
I get this dictionary:

Finding all identifiers containing part of the token

I know I can get a string from resources using
Resources.GetIdentifier(token, "string", ctx.ApplicationContext.PackageName)
(sorry, this is in C#, it's part of a Xamarin.Android project).
I know that if my elements are called foo_1, foo_2, foo_3, then I can iterate and grab the strings using something like
var myList = new List<string>();
for(var i = 0; i < 4; ++i)
{
var id = AppContent.GetIdentifier(token + i.ToString(), "string", "package_name");
if (id != 0)
myList.Add(AppContext.GetString(id));
}
My issue is that my token names all begin with "posn." (the posn can denote the position of anything, so you can have "posn.left_arm" and "posn.brokenose"). I want to be able to add to the list of posn elements, so I can't really store a list of the parts after the period. I can't use a string-array for this either (specific reason means I can't do this).
Is there a way that I can use something akin to "posn.*" in the getidentifer call to return the ids?
You can use some reflection foo to get what you want. It is not pretty at all but it works. The reflection stuff is based on https://gist.github.com/atsushieno/4e66da6e492dfb6c1dd0
private List<string> _stringNames;
private IEnumerable<int> GetIdentifiers(string contains)
{
if (_stringNames == null)
{
var eass = Assembly.GetExecutingAssembly();
Func<Assembly, Type> f = ass =>
ass.GetCustomAttributes(typeof(ResourceDesignerAttribute), true)
.OfType<ResourceDesignerAttribute>()
.Where(ca => ca.IsApplication)
.Select(ca => ass.GetType(ca.FullName))
.FirstOrDefault(ty => ty != null);
var t = f(eass) ??
AppDomain.CurrentDomain.GetAssemblies().Select(ass => f(ass)).FirstOrDefault(ty => ty != null);
if (t != null)
{
var strings = t.GetNestedTypes().FirstOrDefault(n => n.Name == "String");
if (strings != null)
{
var fields = strings.GetFields();
_stringNames = new List<string>();
foreach (var field in fields)
{
_stringNames.Add(field.Name);
}
}
}
}
if (_stringNames != null)
{
var names = _stringNames.Where(s => s.Contains(contains));
foreach (var name in names)
{
yield return Resources.GetIdentifier(name, "string", ComponentName.PackageName);
}
}
}
Then somewhere in your Activity you could do:
var ids = GetIdentifiers("action").ToList();
That will give you all the String Resources, which contain the string action.

How to compare key/value dictionary with == operator on a IReadOnlyCollection<string>?

I have a MultiValueDictionary<string, string> where I am trying to get a key by value.
var dic = na.prevNext; // Getter to get MultiValueDictionary
string nodePointingToThisOne = "";
foreach (var item in dic)
{
if(item.Value == "test")
{
nodePointingToThisOne = item.Key;
}
break;
}
This does not work so I tried Linq:
string nodePointingToThisOne = dic.Where(x => x.Value == this.nodeID).Select(x => x.Key);
But on both I get this error: Operator '==' cannot be applied to operands of type 'System.Collections.Generic.IReadOnlyCollection<string>' and 'string'
So my question is how do I make this comparison work for a read-only collection? I am aware that I get problems if a key exists multiple times but I reduced the problem to this one for now.
I read
Get Dictionary key by using the dictionary value
LINQ: Getting Keys for a given list of Values from Dictionary and vice versa
get dictionary key by value
Getting key of value of a generic Dictionary?
Get key from value - Dictionary<string, List<string>>
but they deal with a "normal" dictionary.
Since the Value property it self may contain multiple values you can't compare it directly against certain string using == operator. Use Contains() instead :
.....
if (item.Value.Contains("test"))
{
.....
}
...or in method chain version :
string nodePointingToThisOne = dic.Where(x => x.Value.Contains("test"))
.Select(x => x.Key)
.FirstOrDefault();
Try to iterate by Keys and then compare the value, and return the Key only if the Value matches.
Like this:
foreach (var key in dic.Keys)
{
if(dic[key].Contains("your value"))
return key;
}
You can iterate over keys like this
foreach (var key in dic.Keys)
{
if(key == "your key")
return key;
}
You can also iterate over values like this
foreach (var v in dic.Values)
{
if(v == "your value")
return v;
}
Example:
Dictionary<string, string> c = new Dictionary<string, string>();
c.Add("Pk", "Pakistan");
c.Add("Aus", "Australia");
c.Add("Ind", "India");
c.Add("Nz", "New Zeland");
c.Add("SA", "South Africa");
foreach (var v in c.Values)
{
if (v == "Australia")
{
Console.WriteLine("Your Value is = " + v);
// perform your task
}
}
foreach (var k in c.Keys)
{
if (k == "Aus")
{
// perform your task
Console.WriteLine("Your Key is = " + k);
}
}
Output:
Your Value is = "Australia"
Your Key is = "Aus"
If you look at the MultiValueDictionary, line 81 will give you a hint. It is:
[TestInitialize]
public void TestInitialize()
{
MultiValueDictionary = new MultiValueDictionary<TestKey, string>();
}
protected static void AssertAreEqual( IDictionary<TestKey, string[]> expected,
IMultiValueDictionary<TestKey, string> actual )
{
Assert.AreEqual( expected.Count, actual.Count );
foreach ( var k in expected.Keys )
{
var expectedValues = expected[ k ];
var actualValues = actual[ k ];
AssertAreEqual( expectedValues, actualValues );
}
}
So for your case, the solution is similar:
foreach (var item in dic.keys)
{
if(dict[item] == "test")
{
nodePointingToThisOne = item;
return nodePointingToThisOne;
}
}
worked for me:
foreach (int i in Dictionary.Keys)
{
if (Dictionary.Values.ToString() == "Your Value")
{
return Dictionary.Keys;
}
}

Inserting value in List of values of a Key in Dictionary

I have a rowsDictionary that its keys point to a list of EmployeeSummary classes.
In those EmployeeSummary classes we also have a string property of Delivery_System
I am looping through this in this way but now stuck in the part that I want to have a deliverySystemFinder dictioanry that its keys are combinedKey as below and the value for each key is a list of distinct delivery_system values
//rowsDictionary is a Dictionary<string, List<EmployeeSummary>>
Dictionary<string, List<string>> deliverySystemFinder = new Dictionary<string, List<string>>();
foreach (string key in rowsDictionary.Keys)
{
List<EmployeeSummary> empList = rowsDictionary[key];
foreach (EmployeeSummary emp in empList)
{
string combinedKey = emp.LastName.Trim().ToUpper() + emp.FirstName.Trim().ToUpper();
string delivery_system = emp.Delivery_System;
// so now I should go and
//A) does deliverySystemFinder have this combinedKey? if not add it.
//B) Does combinedKey in the list of its values already have the value for delivery_system? if it does not then add it
}
}
This would work, for start:
foreach (string key in rowsDictionary.Keys)
{
List<EmployeeSummary> empList = rowsDictionary[key];
foreach (EmployeeSummary emp in empList)
{
string combinedKey = emp.LastName.Trim().ToUpper() +
emp.FirstName.Trim().ToUpper();
string delivery_system = emp.Delivery_System;
List<string> systems = null;
// check if the dictionary contains the list
if (!deliverySystemFinder.TryGetValue(combinedKey, out systems))
{
// if not, create it and add it
systems = new List<string>();
deliverySystemFinder[combinedKey] = systems;
}
// check if the list contains the value and add it
if (!systems.Contains(delivery_system))
systems.Add(delivery_system);
}
}
Now, a couple of remarks:
It doesn't make sense to iterate through Keys, and then do a lookup in each iteration. You can directly iterate KeyValuePairs using a foreach loop.
Using concatenated strings as unique keys often fails. In this case, what happens if you have users { LastName="Some", FirstName="Body" } and { LastName="So", FirstName="Mebody" } in your list?
Checking if a List contains a value is a O(n) operation. You would greatly improve performance if you used a HashSet<string> instead.
Finally, the simplest way to achieve what you're trying to do is to ditch those loops and simply use:
// returns a Dictionary<EmployeeSummary, List<string>>
// which maps each distinct EmployeeSummary into a list of
// distinct delivery systems
var groupByEmployee = rowsDictionary
.SelectMany(kvp => kvp.Value)
.GroupBy(s => s, new EmployeeSummaryEqualityComparer())
.ToDictionary(
s => s.Key,
s => s.Select(x => x.Delivery_System).Distinct().ToList());
With EmployeeSummaryEqualityComparer defined something like:
class EmployeeSummaryEqualityComparer : IEqualityComparer<EmployeeSummary>
{
public bool Equals(EmployeeSummary x, EmployeeSummary y)
{
if (object.ReferenceEquals(x, null))
return object.ReferenceEquals(y, null);
return
x.FirstName == y.FirstName &&
x.LastName == y.LastName &&
... (depending on what constitutes 'equal' for you)
}
public int GetHashCode(EmployeeSummary x)
{
unchecked
{
var h = 31; // null checks might not be necessary?
h = h * 7 + (x.FirstName != null ? x.FirstName.GetHashCode() : 0);
h = h * 7 + (x.LastName != null ? x.LastName.GetHashCode() : 0);
... other properties similarly ...
return h;
}
}
}
If you really think that using the string key will work in all your cases, you can do it without the custom equality comparer:
// returns a Dictionary<string, List<string>>
var groupByEmployee = rowsDictionary
.SelectMany(kvp => kvp.Value)
.GroupBy(s => s.LastName.ToUpper() + s.FirstName.ToUpper())
.ToDictionary(
s => s.Key,
s => s.Select(x => x.Delivery_System).Distinct().ToList());

Categories