Concatenating IEnumerable<KeyValuePair<string,string>> values into string using Linq - c#

Given IEnumerable<KeyValuePair<string,string>>, I'm trying to use linq to concatenate the values into one string.
My Attempt:
string path = attributes.Aggregate((current, next) => "#" + current.Key + "=" + current.Value + " and #" + next.Key + "=" + next.Value);
This produces the error:
Cannot convert expression type 'string' to return type 'KeyValuePair<string,string>'
Is there a more effiecient way to do this in linq?
The full method...
public IEnumerable<XmlNode> GetNodes(IEnumerable<KeyValuePair<string,string>> attributes) {
StateInfoXmlDocument stateInfoXmlDocument = new StateInfoXmlDocument();
string path = attributes.Aggregate((current, next) => "#" + current.Key + "=" + current.Value + " and #" + next.Key + "=" + next.Value);
string schoolTypeXmlPath = string.Format(SCHOOL_TYPE_XML_PATH, path);
return stateInfoXmlDocument.SelectNodes(schoolTypeXmlPath).Cast<XmlNode>().Distinct();
}

Is this what you're looking for?
var strings = attributes.Select(kvp => string.Format("#{0}={1}", kvp.Key, kvp.Value));
string path = string.Join(" and ", strings);

string s = String.Join("#",attributes.Select(kv=>kv.Key+"="+kv.Value));

If you want to use aggregate to make a string you need to use the seeded overload of aggregate
if you use the none seeded version then all the types in the call need to be the same.

string templ = "{0}={1}";
string _authStr = String.Join("&", formParams.Select(kv => String.Format(templ, kv.Key, kv.Value));

Related

How to set prefix in string in join?

I have this strings:
string[] codes = new string[]{"66000110", "66000001", "66000121"};
I want to make join on strings above:
string filter = string.Join(" OR " + some_ID + "=", codes );
The resukt I get is:
some_ID=66000110 OR some_ID=66000001 OR some_ID=66000121
While I need the string like that(OR missing on start string):
OR some_ID=66000110 OR some_ID=66000001 OR some_ID=66000121
How do I fix elegantic way to get OR on start of the string?
It seems that you are building some kind of SQL; if it's your case, try switching to IN:
string filter = $" OR {some_ID} IN ({string.Join(", ", codes)})";
And you'll get a more readable equivalent
" OR some_ID IN (66000110, 66000001, 66000121)"
You can use a combination of LINQ and Concat():
string filter = string.Concat(codes.Select(c => " OR " + some_ID + "=" + c));
Why not something like this?
string filter = " or " + string.Join(" OR " + some_ID + "=", codes );

Passing whole function as lambda expression to LINQ

I've getting auctions from my database as:
var auctions = from o in db.auctions select o;
I'd like to pass this function as lambda expression in .Where clause of my linq to filter my auctions result:
private bool wordsHasProductName(auction a, string[] words)
{
if (!a.product_name.Contains(' ')) // Auction product name is single word - check if
{
if (words.Length > 1) return false; // array is passed, single word is searching
else return a.product_name.Contains(words[0]); // check if product name consists passed string
}
else // Auction product name has more words check if passed string is partially in this product name
{
string[] productName = a.product_name.Split(' ');
var list = new List<string>(words);
foreach (string item in productName)
{
if (list.Contains(item)) list.Remove(item);
}
return list.Count == 0;
}
}
I'm calling it in my code, and while debugging there's no error at this line, here's place where I call .Where() clause in linq:
string[] words = searchName.Split(' ');
auctions = auctions.Where((a) => wordsHasProductName(a, words));
But at the end at return statement I'm getting exception error:
return View(auctions.ToPagedList(pageNumber, pageSize));
Error code:
An exception of type 'System.NotSupportedException' occurred in
EntityFramework.SqlServer.dll but was not handled in user code
Additional information: LINQ to Entities does not recognize the method
'Boolean wordsHasProductName(IEP_Projekat.Models.auction,
System.String[])' method, and this method cannot be translated into a
store expression.
How could I succeed sending full bool function as lambda expression in my linq .Where()?
As others already mentioned, method calls cannot be used in LINQ to Entities because they cannot be translated to SQL. So the only possible way would be if you can rewrite the method as compatible expression. And here comes the other problem - you are using string.Split method which is not supported.
So you are out of luck. Or may be not. Here is the trick. Instead of splitting the product_name and checking if it contains every word in words, you can use the following (IMO equivalent) criteria:
words.All(word => (" " + product_name + " ").Contains(" " + word + " "))
Enclosing both product_name and word with space allows you to match the whole word at the beginning, middle or at the end of the target string. And it uses only constructs that are supported by the EF.
Putting it all together. The function would be like this:
private static Expression<Func<auction, bool>> wordsHasProductName(string[] words)
{
if (words.Length == 1)
{
var word = words[0]; // Avoid ArrayIndex not supported
return a => !a.product_name.Contains(" ") && a.product_name.Contains(word);
}
else
{
return a => words.All(word => (" " + a.product_name + " ").Contains(" " + word + " "));
}
}
and the usage:
string[] words = searchName.Split(' ');
auctions = auctions.Where(wordsHasProductName(words));
However the above implementation does not produce very good SQL query, especially for more than one word. A much better SQL is produced if rewriting it by building the predicate expression manually:
private static Expression<Func<auction, bool>> wordsHasProductName(string[] words)
{
Expression<Func<auction, string>> product_name;
Expression condition;
var containsMethod = typeof(string).GetMethod("Contains", new[] { typeof(string) });
if (words.Length == 1)
{
// a => !a.product_name.Contains(" ") && a.product_name.Contains(words[0])
product_name = a => a.product_name;
condition = Expression.AndAlso(
Expression.Not(Expression.Call(product_name.Body, containsMethod, Expression.Constant(" "))),
Expression.Call(product_name.Body, containsMethod, Expression.Constant(words[0])));
}
else
{
// a => (" " + a.product_name + " ").Contains(" " + words[0] + " ")
// && (" " + a.product_name + " ").Contains(" " + words[1] + " ")
// ...
// && (" " + a.product_name + " ").Contains(" " + words[N-1] + " ")
product_name = a => " " + a.product_name + " ";
condition = words
.Select(word => Expression.Call(product_name.Body, containsMethod, Expression.Constant(" " + word + " ")))
.Aggregate<Expression>(Expression.AndAlso);
}
return Expression.Lambda<Func<auction, bool>>(condition, product_name.Parameters);
}
Welcome to the world of EF and expressions :)
You can't pass this complex C# logic as an Expression Tree that can interpreted by the EF provider and the hardest part for the provider is transforming it into SQL statements.
So, your option is to create a stored procedure where you pass in the required parameters, then you write the logic using pure SQL. Then, you map your SP to EF and call it to return the list of objects you want.

Array of string into a string ready for JSON formatting

I have an array of string values obtained from a method and I want to convert this array into a HTML readable format for getting/posting (eg. value=[12,21])
I have tried the following:
string[] array1 = methodToGetStringArray(); //assuming [12,21] for example
string finalString = "value="+array1; //intended output is value=[12,21]
Which of course doesn't work.
I would like to know the method to provided the value as shown above.
You can try,
string finalString = String.Format("value=[{0}]", string.Join(", ", array1));
finalString should return,
value=[12, 21]
Try like this:
string[] array1 = methodToGetStringArray();
string json = JsonConvert.SerializeObject(array1);
Refer JSON.NET
you can try this
string finalString = "Value = [" + string.Join(",", array1) + "]";
Use string.Join method:
string finalString = "value=[" + string.Join(",",array1) + "]";
Or JavaScriptSerializer:
var serializer = new JavaScriptSerializer();
var finalString = "value=" + serializer.Serialize(array1);
List<string> list = new List<string>(array1);
var a = "value=[" + list.Aggregate((x, y) => x + "," + y) + "]";

Replace closest instance of a word

I have a string like this:
“I’m a member of the Imperial Senate on a diplomatic mission to Alderaan.”
I want to insert <strong> around the "a" in "a diplomatic", but nowhere else.
What I have as input is diplomatic from a previous function, and I wan't to add <strong>to the closest instance of "a".
Right now, of course when I use .Replace("a", "<strong>a</strong>"), every single instance of "a" receives the <strong>-treatment, but is there any way to apply this to just to one I want?
Edit
The string and word/char ("a" in the case above) could be anything, as I'm looping through a lot of these, so the solution has to be dynamic.
var stringyourusing = "";
var letter = "";
var regex = new Regex(Regex.Escape(letter));
var newText = regex.Replace(stringyourusing , "<strong>letter</strong>", 1);
Would this suffice?
string MakeStrongBefore(string strong, string before, string s)
{
return s.Replace(strong + " " + subject, "<strong>" + strong + "</strong> " + before);
}
Used like this:
string s = “I’m a member of the Imperial Senate on a diplomatic mission to Alderaan.”;
string bolded = MakeStrongBefore("a", "diplomatic", s);
Try this:
public string BoldBeforeString(string source, string bolded,
int boldBeforePosition)
{
string beforeSelected = source.Substring(0, boldBeforePosition).TrimEnd();
int testedWordStartIndex = beforeSelected.LastIndexOf(' ') + 1;
string boldedString;
if (beforeSelected.Substring(testedWordStartIndex).Equals(bolded))
{
boldedString = source.Substring(0, testedWordStartIndex) +
"<strong>" + bolded + "</strong>" +
source.Substring(testedWordStartIndex + bolded.Length);
}
else
{
boldedString = source;
}
return boldedString;
}
string phrase = "I’m a member of the Imperial Senate on a diplomatic mission to Alderaan.";
string boldedPhrase = BoldBeforeString(phrase, "a", 41);
Hei!
I've tested this and it works:
String replaced = Regex.Replace(
"I’m a member of the Imperial Senate on a diplomatic mission to Alderaan.",
#"(a) diplomatic",
match => "<strong>" + match.Result("$1") + "</strong>");
So to make it a general function:
public static String StrongReplace(String sentence, String toStrong, String wordAfterStrong)
{
return Regex.Replace(
sentence,
#"("+Regex.Escape(toStrong)+") " + Regex.Escape(wordAfterStrong),
match => "<strong>" + match.Result("$1") + "</strong>");
}
Usage:
String sentence = "I’m a member of the Imperial Senate on a diplomatic mission to Alderaan.";
String replaced = StrongReplace(sentence, "a", "diplomatic");
edit:
considering your other comments, this is a function for placing strong tags around each word surrounding the search word:
public static String StrongReplace(String sentence, String word)
{
return Regex.Replace(
sentence,
#"(\w+) " + Regex.Escape(word) + #" (\w+)",
match => "<strong>" + match.Result("$1") + "</strong> " + word + " <strong>" + match.Result("$2") + "</strong>");
}

LINQ query Concat values to list of type string

Is there a more efficient way to handle this?
List<String> lstReferences = (from f in
(from section in courseSectionToCreate.SectionsToAdd
select new {
ReferenceNumber = section.Course.CourseNumber.Substring(0, 5) + "." +
section.Course.CourseNumber.Substring(5) + "." +
section.Session + "." +
section.Year + "." +
section.SectionNumber + ";"
})
select f.ReferenceNumber).ToList();
strReferenceNumber = lstReferences.Aggregate((a, b) => a + ", " + b);
Yes, you definitely don't want to be using Aggregate here. That is O(n^2) (it's Schlemiel the Painter's algorithm). Instead:
string referenceNumber = String.Join(", ", lstReferences);
This is better because String.Join will use a StringBuilder internally.
You can replace all that with this:
var strReferenceNumber =
String.Join(", ",
courseSectionToCreate.SectionsToAdd.Select(s =>
String.Join(".",
s.Course.CourseNumber.Substring(0, 5),
s.Course.CourseNumber.Substring(5),
s.Session,
s.Year,
s.SectionNumber) + ";"
)
);
How about:
var lstReferences = from section in courseSectionToCreate.SectionsToAdd
let courseNumber = section.Course.CourseNumber
let toJoin = new object[]
{
courseNumber.Substring(0, 5),
courseNumber.Substring(5),
section.Session,
section.Year,
section.SectionNumber
}
select string.Join(".", toJoin) + ";"
var strReferenceNumber = string.Join(", ", lstReferences);

Categories

Resources