C# Linq to XML - Return multiple children - c#

I have this xml:
<?xml version="1.0" encoding="utf-8" ?>
<Interfaces>
<Interface>
<Name>Account Lookup</Name>
<PossibleResponses>
<Response>Account OK to process</Response>
<Response>Overridable restriction</Response>
</PossibleResponses>
</Interface>
<Interface>
<Name>Balance Inquiry</Name>
<PossibleResponses>
<Response>Funds available</Response>
<Response>No funds</Response>
</PossibleResponses>
</Interface>
</Interfaces>
I need to retrieve the possible responses for an interface:
// Object was loaded with XML beforehand
public class Interfaces : XElement {
public List<string> GetActionsForInterface(string interfaceName) {
List<string> actionList = new List<string>();
var actions = from i in this.Elements("Interface")
where i.Element("Name").Value == interfaceName
select i.Element("PossibleResponses").Element("Response").Value;
foreach (var action in actions)
actionList.Add(action);
return actionList;
}
}
The result should be a list such as this (for interface 'Account Lookup'):
Account OK to process
Overridable restriction
But its only returning the first value - 'Account OK to process'. What is wrong here?
Edit:
I changed my method:
public List<string> GetActionsForInterface(string interfaceName) {
List<string> actionList = new List<string>();
var actions = from i in this.Elements("interface")
where i.Element("name").Value == interfaceName
select i.Element("possibleresponses").Elements("response").Select(X => X.Value);
foreach (var action in actions)
actionList.Add(action);
return actionList;
}
But now I get 2 errors on line 'actionList.Add(action);':
The best overloaded method match for System.Collections.Generic.List<string>.Add(string)' has some invalid arguments
Argument 1: cannot convert from 'System.Collections.Generic.IEnumerable<char>' to 'string'
I suppose the select many is casting the results into something else then strings?
Edit:
To fix the last error:
foreach (var actions in query)
foreach(string action in actions)
actionList.Add(action);
Apparently there is an array within an array here.

This
select i.Element("PossibleResponses").Element("Response")
returns the first "response" element. Use Elements instead.
You then need to select many to get the values.

doc.Root.Elements("Interface").Select(e=>new {
Name = e.Element("Name").Value,
PossibleResponses = e.Element("PossibleResponses").Elements("Response").select(e2=>e2.Value)
});

static List<string> GetActionsForInterface(string interfaceName)
{
var doc = XDocument.Parse(xml);
List<string> actionList = new List<string>();
var actions = doc.Root
.Elements("Interface")
.Where(x => x.Element("Name").Value == interfaceName).
Descendants("Response").Select(x => x.Value);
foreach (var action in actions)
actionList.Add(action);
return actionList;
}

Related

Use Foreach loop in MVC

How can I process the foreach loop in MVC. In the bellow controller I am calling a method called SendSimpleMessage() which sends an email to its parameter input but on my controller I am unable to use foreach properly.
[HttpPost]
public ActionResult EmailCampaignProcess(FormCollection collection)
{
//var userType = Request["userType"];
//var emailContent = Request["emailContent"];
//SendSimpleMessage();
//ViewBag.Message = "Hello " + Request["userType"];
var Emails = db.Users.Where(d => d.Subscriptions.Any(x => x.Status == true)).Select(u => u.Email).ToArray();
foreach (Emails as Email) {
SendSimpleMessage(Email);
}
}
Your code is wrong, a foreach loop should look like
foreach (var currentEmail in Emails) { //where var can be your Class maybe Email
SendSimpleMessage(currentEmail);
}
Generally a foreach looks like:
foreach(T objectName in YourCollection){
//T is your class
//objectName is the way you access the object within your loop
//in references the list
//YourCollection is IEnumerable<T>
}
Defintion of ForEach Statement
The for each statement is used to iterate through a collection. You can modify elements in a collection, but you cannot add or delete elements.The statements are executed for each element in the array or collection. After the iteration has been completed for all the elements in the collection, control is transferred to the statement that follows the for each block
Syntax:
for each (type identifier in expression)
{
statements
}
Parameters type
The type of identifier.
identifier
The iteration variable that represents the collection element. When identifier is a Tracking Reference Operator, you can modify the element.
expression
An array expression or collection. The collection element must be such that the compiler can convert it to the identifier type.
statements
One or more statements to be executed.
Simple Example:
string[] countries = { "india", "US", "UK" };
foreach (string value in countries )
{
Console.WriteLine(value);
}
In the same way your code will change like below:
[HttpPost]
public ActionResult EmailCampaignProcess(FormCollection collection)
{
//var userType = Request["userType"];
//var emailContent = Request["emailContent"];
//SendSimpleMessage();
//ViewBag.Message = "Hello " + Request["userType"];
var Emails = db.Users.Where(d => d.Subscriptions.Any(x => x.Status == true)).Select(u => u.Email).ToArray();
foreach (string SingleEmail in Emails) {
SendSimpleMessage(SingleEmail);
}
// Or if you are not sure about the outcome type you can use the var keyword like below
foreach (var SingleEmail in Emails) {
SendSimpleMessage(SingleEmail);
}
}
Hope the above information was helpful

How to get list items without for each in MVC controller

I need to access items from a list which is i am accessing with the following code
foreach (var reportval in reportresponse)
{
foreach (var valitem in reportval.Comparison)
{
var responsemodel = new ReportResponseModel();
responsemodel.StudentVariable = valitem.StudentVariable;
responsemodel.Lighter = valitem.Lighter;
responsemodel.Matched = valitem.Matched;
responsemodel.Stronger = valitem.Stronger;
reportResponseModel.Add(responsemodel);
}
};
I tried the following code to exit the loop without retiterting the first loop. But its not working.
if (reportresponse.Count == reportResponseModel.Count) { break; };
also i tried the following way to access the inner list from the first loop but its not accessible here
foreach (var reportval in reportresponse.Comparison)
Can someone please help on this?
Did you try like this
foreach (var reportval in reportresponse.Comparison)
{
// var responsemodel = new ReportResponseModel();
responsemodel.StudentVariable = valitem.StudentVariable;
responsemodel.Lighter = reportval .Lighter;
responsemodel.Matched = reportval .Matched;
responsemodel.Stronger = reportval .Stronger;
//reportResponseModel.Add(responsemodel);
}
};
Have you tried Linq's SelectMany?
You can do something like
var reportResponseModel = reportresponse.SelectMany(r => r.Comparison, (r, c) =>
new ReportResponseModel
{
StudentVariable = c.StudentVariable,
Lighter = c.Lighter,
Matched = c.Matched,
Stronger = c.Stronger
});
Then you can apply additional filtering like with .Where or .Take to have only needed count of items.
I updated the code with the following changes.
if (reportresponse.Count == reportResponseModel.Count)
{ return reportResponseModel; };
after
reportResponseModel.Add(responsemodel);
Thanks #Stephen Muecke !!

Updating sort column in ASP.NET with Request.QueryString Array

I'm passing a list of guids in a GET request from a JQuery Ajax call.
on my ASP.NET controller side I want to iterate through the list and update the Display_Sort column to match my newly sorted list.
My ID is a Guid and I'm getting a type error in the following code, because it's a string that I'm passing to the Db. However, I can't seem to convert the item(string) into a Guid.
I've tried Guid(item) and it would allow the constructor. Not sure what I'm missing.
Here is the code:
//REORDER HOME ASSETS
public ActionResult ReOrderHome()
{
using (var db = new IFEntities())
{
var myString = Request.QueryString;
var i = 1;
foreach (var item in myString)
{
var myObj = db.HomeContents.Find(item);
myObj.display_order = i;
db.SaveChanges();
i++;
}
}
You can convert item to GUID and then compare like this.
var myObj = db.HomeContents.Find(new Guid(item));
Or, you can use select instead of find. Syntax for select --
foreach (var item in myString)
{
var myObj = db.HomeContents.Select(p => p.<GUID_COLUMN_NAME> == item);
myObj.display_order = i;
db.SaveChanges();
i++;
}
Replace GUID_COLUMN_NAME with actual column name.

XML output contains duplicate values after creating it using C# Linq to XML

Here is my code for creating an XML file
public void CreateXml(string[] names, string[] values, string type)
{
XElement xml = new XElement("Transaction",
new XElement("TransactionType", type));
foreach (var i in names)
{
foreach (var o in values)
{
xml.Add(new XElement(i.Replace(" ", string.Empty), o));
}
}
xml.Save("C:\\Users\\PHWS13\\Desktop\\sample.xml");
}
And my output looks like this
<?xml version="1.0" encoding="UTF-8"?>
<Transaction>
<TransactionType>Void</TransactionType>
<Zip>1</Zip>
<Zip>2</Zip>
<PNRef>1</PNRef>
<PNRef>2</PNRef>
</Transaction>
But it is not right, I am expecting more like this
<?xml version="1.0" encoding="UTF-8"?>
<Transaction>
<TransactionType>Void</TransactionType>
<Zip>1</Zip>
<PNRef>2</PNRef>
</Transaction>
As I have noticed the values are correct but it i just duplicates, How can I fix this?
string[] textBoxNamesArray = flowLayoutPanelText.Controls.OfType<TextBox>()
.Select(r => r.Name)
.ToArray();
string[] textBoxTextsArray = flowLayoutPanelText.Controls.OfType<TextBox>()
.Select(r => r.Text)
.ToArray();
You shouldn't use two nested loops then: two nested loops create a Cartesian product (i.e. every name is paired up with every value).
If you use a single loop that goes through names and values at the same time, you'd get your expected output.
If you are using .NET 4, you could use Zip method, like this:
xml.Add(
names
.Zip(values, (n,v) => new XElement(n.Name(" ", string.Empty), v))
.ToArray()
);
Hard to tell without knowing how you structure the data in your 2 arrays, but I would guess:
for (int i = 0; i < names.Length; i++)
{
xml.Add(new XElement(names[i].Replace(" ", string.Empty), values[i]));
}
Try this using a dictionary.
public void CreateXml(Dictionary<string, string> myNameValuePairs, string type)
{
XElement xml = new XElement("Transaction",
new XElement("TransactionType", type));
foreach (var key in names)
{
xml.Add(new XElement(key.Replace(" ", string.Empty), myNameValuePairs[key]));
}
xml.Save("C:\\Users\\PHWS13\\Desktop\\sample.xml");
}
Edit: Workaround using your method signature as above. But this works if an only if values[i] is the corresponding value for names[i]. I would not advise this unless you are sure about the array lengths and contens of names and values.
public void CreateXml(string[] names, string[] values, string type)
{
XElement xml = new XElement("Transaction",
new XElement("TransactionType", type));
foreach (var i = 0; i < names.length; i++)
{
xml.Add(new XElement(names[i].Replace(" ", string.Empty), values[i]));
}
xml.Save("C:\\Users\\PHWS13\\Desktop\\sample.xml");
}

C# dedupe List based on split

I'm having a hard time deduping a list based on a specific delimiter.
For example I have 4 strings like below:
apple|pear|fruit|basket
orange|mango|fruit|turtle
purple|red|black|green
hero|thor|ironman|hulk
In this example I should want my list to only have unique values in column 3, so it would result in an List that looks like this,
apple|pear|fruit|basket
purple|red|black|green
hero|thor|ironman|hulk
In the above example I would have gotten rid of line 2 because line 1 had the same result in column 3. Any help would be awesome, deduping is tough in C#.
how i'm testing this:
static void Main(string[] args)
{
BeginListSet = new List<string>();
startHashSet();
}
public static List<string> BeginListSet { get; set; }
public static void startHashSet()
{
string[] BeginFileLine = File.ReadAllLines(#"C:\testit.txt");
foreach (string begLine in BeginFileLine)
{
BeginListSet.Add(begLine);
}
}
public static IEnumerable<string> Dedupe(IEnumerable<string> list, char seperator, int keyIndex)
{
var hashset = new HashSet<string>();
foreach (string item in list)
{
var array = item.Split(seperator);
if (hashset.Add(array[keyIndex]))
yield return item;
}
}
Something like this should work for you
static IEnumerable<string> Dedupe(this IEnumerable<string> input, char seperator, int keyIndex)
{
var hashset = new HashSet<string>();
foreach (string item in input)
{
var array = item.Split(seperator);
if (hashset.Add(array[keyIndex]))
yield return item;
}
}
...
var list = new string[]
{
"apple|pear|fruit|basket",
"orange|mango|fruit|turtle",
"purple|red|black|green",
"hero|thor|ironman|hulk"
};
foreach (string item in list.Dedupe('|', 2))
Console.WriteLine(item);
Edit: In the linked question Distinct() with Lambda, Jon Skeet presents the idea in a much better fashion, in the form of a DistinctBy custom method. While similar, his is far more reusable than the idea presented here.
Using his method, you could write
var deduped = list.DistinctBy(item => item.Split('|')[2]);
And you could later reuse the same method to "dedupe" another list of objects of a different type by a key of possibly yet another type.
Try this:
var list = new string[]
{
"apple|pear|fruit|basket",
"orange|mango|fruit|turtle",
"purple|red|black|green",
"hero|thor|ironman|hulk "
};
var dedup = new List<string>();
var filtered = new List<string>();
foreach (var s in list)
{
var filter = s.Split('|')[2];
if (dedup.Contains(filter)) continue;
filtered.Add(s);
dedup.Add(filter);
}
// Console.WriteLine(filtered);
Can you use a HashSet instead? That will eliminate dupes automatically for you as they are added.
May be you can sort the words with delimited | on alphabetical order. Then store them onto grid (columns). Then when you try to insert, just check if there is column having a word which starting with this char.
If LINQ is an option, you can do something like this:
// assume strings is a collection of strings
List<string> list = strings.Select(a => a.Split('|')) // split each line by '|'
.GroupBy(a => a[2]) // group by third column
.Select(a => a.First()) // select first line from each group
.Select(a => string.Join("|", a))
.ToList(); // convert to list of strings
Edit (per Jeff Mercado's comment), this can be simplified further:
List<string> list =
strings.GroupBy(a => a.split('|')[2]) // group by third column
.Select(a => a.First()) // select first line from each group
.ToList(); // convert to list of strings

Categories

Resources