I am trying to write the output of a LINQ query into a text file. For that I am using an extension method.
This is my LINQ query:
var group =
from c in census_data
group c by c.state into g
join s in state_gdp on g.FirstOrDefault().state equals s.state
orderby s.gdp descending
select new
{
State = g.Key,
Count = g.Count(),
SavingsBalance = g.Average(x => x.savingsBalanceDouble),
GDP = s.gdp
};
This is my extension method:
public static class CSVWriter
{
public static void write(this Enumerable e, string file)
{
using (System.IO.StreamWriter f = new System.IO.StreamWriter(file))
{
foreach (var i in e)
{
f.WriteLine(i);
}
}
}
}
However I am getting an error that says System.Linq.Enumerable does not have a getEnumerator method.
A possible solution can look like this:
var result =
from c in census_data
group c by c.state into g
join s in state_gdp on g.FirstOrDefault().state equals s.state
orderby s.gdp descending
select new
{
State = g.Key,
Count = g.Count(),
SavingsBalance = g.Average(x => x.savingsBalanceDouble),
GDP = s.gdp
};
var buffer = new StringBuilder();
buffer.AppendLine("#key,name,sum,gdp");
result.ToList().ForEach(item => buffer.AppendLine(String.Format("{0},{1},{2},{3}", item.State, item.Count, item.SavingBalance, item.GDP)));
File.WriteAllText("d:\\temp\\file.csv", buffer.ToString());
You need to change Enumerable to IEnumerable. Since you are creating an anonymous object, your solution will transfer the list of anonymous objects (IEnumerable<anonymous>) to your function where you write the data to the file, but you will not be able to format the ouput as desired.
One possible solution would be to put the lines you want to write to the file to a string buffer and then write the text at once using the System.IO.File.WriteAllText method:
// test data
var data = new List<Int32> { 1, 20, 30, 40, 50, 70 };
// create a list of anonymous objects
var result = data.Select (d => new
{
Count = d,
State = String.Format("Item {0}", d),
SavingBalance = d * 10
});
// create the output text buffer
var buffer = new StringBuilder();
// add header line
buffer.AppendLine("#key,name,sum");
// add each result line
result.ToList().ForEach(item => buffer.AppendLine(String.Format("{0},{1},{2}", item.Count, item.State, item.SavingBalance)));
// write to file
File.WriteAllText("d:\\temp\\file.csv", buffer.ToString());
The output is:
#key,name,sum
1,Item 1,10
20,Item 20,200
30,Item 30,300
40,Item 40,400
50,Item 50,500
70,Item 70,700
The solution which #aravol and #StephneKennedy mentioned will look like this:
public static class CSVWriter
{
public static void write<T>(this IEnumerable<T> e, string file)
{
using (System.IO.StreamWriter f = new System.IO.StreamWriter(file))
{
foreach (var i in e)
{
f.WriteLine(i);
}
}
}
}
and can be used like this:
result.write<object>(file);
As already stated, the problem with this solution is that you can not format the output, because you are using the Object.ToString method and you can't format it (the default output looks something like { key = value, key = value, ... }).
If you still want to transfer the result to another method, then create a typed class and create an object for every result entry (and then transfer the list). An example typed class can look like this:
public class Placeholder
{
public String Name { get; set; }
public Int32 Index { get; set; }
public Double Sum { get; set; }
}
Then change your LINQ query to create a new object of Placeholder, instead of anonymous object:
// test data
var data = new List<Int32> { 1, 20, 30, 40, 50, 70 };
var result = data.Select (d => new Placeholder
{
Key = d,
Name = String.Format("Item {0}", d),
Sum = d * 10.0m
}).ToList();
result.write<Placeholder>("d:\\temp\\file.csv");
And your extension method can directly use the write(this IEnumerable<Placeholder>...) or cast every object to use the class properties:
public static class CSVWriter
{
public static void write<T>(this IEnumerable<T> e, string file)
{
using (System.IO.StreamWriter f = new System.IO.StreamWriter(file))
{
foreach (var i in e)
{
f.WriteLine(((Placeholder)i).Sum);
}
}
}
}
Related
I am trying to pull file names that match the substring using "contains" method. However, return seem to be List<char> but I expect List<string>.
private void readAllAttribues()
{
using (var reader = new StreamReader(attribute_file))
{
//List<string> AllLines = new List<string>();
List<FileNameAttributeList> AllAttributes = new List<FileNameAttributeList>();
while (!reader.EndOfStream)
{
FileNameAttributeList Attributes = new FileNameAttributeList();
Attributes ImageAttributes = new Attributes();
Point XY = new Point();
string lineItem = reader.ReadLine();
//AllLines.Add(lineItem);
var values = lineItem.Split(',');
Attributes.ImageFileName = values[1];
XY.X = Convert.ToInt16(values[3]);
XY.Y = Convert.ToInt16(values[4]);
ImageAttributes.Location = XY;
ImageAttributes.Radius = Convert.ToInt16(values[5]);
ImageAttributes.Area = Convert.ToInt16(values[6]);
AllAttributes.Add(Attributes);
}
List<string> unique_raw_filenames = AllAttributes.Where(x => x.ImageFileName.Contains(#"non")).FirstOrDefault().ImageFileName.ToList();
List<string>var unique_reference_filenames = AllAttributes.Where(x => x.ImageFileName.Contains(#"ref")).FirstOrDefault().ImageFileName.ToList();
foreach (var unique_raw_filename in unique_raw_filenames)
{
var raw_attributes = AllAttributes.Where(x => x.ImageFileName == unique_raw_filename).ToList();
}
}
}
Datatype class
public class FileNameAttributeList
{ // Do not change the order
public string ImageFileName { get; set; }
public List<Attributes> Attributes { get; set; }
public FileNameAttributeList()
{
Attributes = new List<Attributes>();
}
}
Why is FirstOrDefault() does not work ? (It returns List<char> but I am expecting List<string> and fails.
The ToList() method converts collections that implement IEnumerable<SomeType> into lists.
Looking at the definition of String, you can see that it implements IEnumerable<Char>, and so ImageFileName.ToList() in the following code will return a List<char>.
AllAttributes.Where(x =>
x.ImageFileName.Contains(#"non")).FirstOrDefault().ImageFileName.ToList();
Although I'm guessing at what you want, it seems like you want to filter AllAttributes based on the ImageFileName, and then get a list of those file names. If that's the case, you can use something like this:
var unique_raw_filenames = AllAttributes.Where(x => x.ImageFileName.Contains(#"non")).Select(y=>y.ImageFileName).ToList();
In your code
List<string> unique_raw_filenames = AllAttributes.Where(x => x.ImageFileName.Contains(#"non")).FirstOrDefault().ImageFileName.ToList();
FirstOrDefault() returns the first, or default, FileNameAttributeList from the list AllAttributes where the ImageFileName contains the text non.
Calling ToList() on the ImageFileName then converts the string value into a list of chars because string is a collection of char.
I think that what you are intending can be achieved by switching out FirstOrDefault to Select. Select allows you to map one value onto another.
So your code could look like this instead.
List<string> unique_raw_filenames = AllAttributes.Where(x => x.ImageFileName.Contains(#"non")).Select(x => x.ImageFileName).ToList();
This then gives you a list of string.
I am trying to get the some specific fields from dynamic object with is actually a list of any class, this class contains various fields out of those fields I want to select some specific fields using LINQ, The fields which I want to select is also passing by the user. Below is the code that I have tried using System.Linq.Dynamic.
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using System.Linq.Dynamic;
using System.Collections;
private void Test_Click(object sender, EventArgs e)
{
List<RateInfo> lst = new List<RateInfo>();
lst.Add(new RateInfo() { id_country = "IND", id_state = 1, rate = 2.3f });
lst.Add(new RateInfo() { id_country = "IND", id_state = 2, rate = 1.1f });
lst.Add(new RateInfo() { id_country = "IND", id_state = 3, rate = 5.2f });
lst.Add(new RateInfo() { id_country = "IND", id_state = 4, rate = 6.5f });
GetDynamicData(lst, new List<string>() { "id_country", "id_state" });
}
private void GetDynamicData(dynamic list, List<string> fetchFields)
{
var data = ((IEnumerable)list).Cast<dynamic>()
.Select(r => new { r }).AsQueryable();
StringBuilder s = new StringBuilder();
//This is for test only.
//It works, the value of "id_state" and "id_state" getting appended
foreach (var item in data)
{
s.Append(item.r.id_state);
s.Append(",");
s.Append(item.r.id_country);
}
//-----------------------------------------------------
//Select the specific field data from dynamic list
StringBuilder fields = new StringBuilder();
fields.Append("new (");
foreach (var fld in fetchFields)
{
fields.Append("r." + fld);
fields.Append(",");
}
fields.Remove(fields.Length - 1, 1);
fields.Append(")");
//This does not work throws error
//"No property or field 'id_country' exists in type 'Object'"
IQueryable iq = data.Select(fields.ToString());
//For test only to check the value of selected fields
foreach (dynamic item in iq)
{
s.Append(item.id_state);
s.Append(",");
s.Append(item.id_country);
}
}
you can hughly simplify your GetDynamicData method both specifying explicit list type (GetDynamicData(IList<RateInfo> list, ...)) and leaving the list item type generic, in order to reuse the method; with this last approach in mind, you can rewrite the GetDynamicData as follows, obtaining the desired output:
private void GetDynamicData<T>(IEnumerable<T> list, List<string> fetchFields)
{
var fields = $"new ({string.Join(",", fetchFields)})";
var res = list.AsQueryable().Select(fields);
//For test only to check the value of selected fields
foreach (dynamic item in res) {
Console.WriteLine(item.id_state);
Console.WriteLine(item.id_country);
}
}
OUTPUT
1
IND
2
IND
3
IND
4
IND
EXPLANATION
I think the difference is that specifying explicitly the type (through generic T or through RateInfo) you force LINQ to know list items'type; if you use dynamic the IQueryable.ElementType of the IQuqryable instance has value System.Object, so the query fails with the error you've experienced.
You should try using generics:
private void GetDynamicData<T>(IEnumerable<T> list, List<string> fetchFields)
{
var data = list.AsQueryable();
I need to fill an array with 60 values type int, and I have the values inside a DbSet in the database.
Without making a loop, is there a way I can conver this List to a Int[].
The value is in a property called temperature
public void SetLineChartData()
{
//Suppose we have a list of 60 items.
using (ZigBeeContext db = new ZigBeeContext())
{
var lista = (from p in db.Medidas
select new Medida
{
Fecha = p.FechaHora
}).ToList();
}
lineChartData = new int[60];
lineChartData[0] = RandomNumberGenerator.randomScalingFactor();
hora[0] = DateTime.Now.ToShortTimeString();
lineChartData[1] = RandomNumberGenerator.randomScalingFactor();
hora[1] = DateTime.Now.ToShortTimeString();
lineChartData[2] = RandomNumberGenerator.randomScalingFactor();
hora[2] = DateTime.Now.ToShortTimeString();
lineChartData[3] = RandomNumberGenerator.randomScalingFactor();
hora[3] = DateTime.Now.ToShortTimeString();
lineChartData[4] = RandomNumberGenerator.randomScalingFactor();
hora[4] = DateTime.Now.ToShortTimeString();
lineChartData[5] = RandomNumberGenerator.randomScalingFactor();
hora[5] = DateTime.Now.ToShortTimeString();
lineChartData[6] = RandomNumberGenerator.randomScalingFactor();
hora[6] = DateTime.Now.ToShortTimeString();
//colorString = "rgba(" + RandomNumberGenerator.randomColorFactor() + "," + RandomNumberGenerator.randomColorFactor() + "," + RandomNumberGenerator.randomColorFactor() + ",.3)";
}
I need to fill an array with 60 values type int, and I have the values inside a DbSet in the database.
Without making a loop, is there a way I can conver this List to a Int[]. The value is in a property called temperature
Not sure what you mean by "Without making a loop" as many functions will perform loops even if they don't look like it.
If I understand what you are trying to do, then maybe something like this:
int[] myArray = lista.Select(x => x.temperature).ToArray();
LINQ can provide what you are asking for:
var arr = lista.Select(i => i.Fecha).ToArray();
With Linq you can do that like this :
List<MyObject> lst = new List<MyObject>(); // fake dbSet can be Queryable
int[] toto = lst.Take(60).Select(item => item.MyInt).ToArray();
private class MyObject
{
public int MyInt { get; set; }
}
Just use Linq:
public void SetLineChartData()
{
int[] yourIntArray; // your int array
//Suppose we have a list of 60 items.
using (ZigBeeContext db = new ZigBeeContext())
{
var lista = (from p in db.Medidas
select new Medida
{
Fecha = p.FechaHora,
}).ToList();
// here is how you can do that
yourIntArray = lista.Select(x =>
x.FechaHora //i think that's property that you need to be in int array
).ToArray();
}
}
If FechaHora is not an int already, and you want to cast it, you could do this...
int[] lista = db.Medidas.Select(p => p.FechaHora).Cast<int>().ToArray();
I have a linq query that selects all textboxes in a placeholder and adds them to a list using a struct. I need to expand this functionality to also take the selectedvalue of a DropDownList I am pretty sure I am doing this wrong, because when I debug the method the lists count is 0.
My own guess is that declaring 2 OfType<>() is wrong, but I am pretty new to linq and I have no idea of how else to do it.
Any help would be awesome! Thanks in advance.
Here's what I have so far:
public struct content
{
public string name;
public string memberNo;
public int points;
public string carclass;
}
List<content> rows = new List<content>();
protected void LinkButton_Submit_Attendees_Click(object sender, EventArgs e)
{
List<content> rows = PlaceHolder_ForEntries.Controls.OfType<TextBox>().OfType<DropDownList>()
.Select(txt => new
{
Txt = txt,
Number = new String(txt.ID.SkipWhile(c => !Char.IsDigit(c)).ToArray())
})
.GroupBy(x => x.Number)
.Select(g => new content
{
carclass = g.First(x => x.Txt.ID.StartsWith("DropDownlist_CarClass")).Txt.SelectedValue,
name = g.First(x => x.Txt.ID.StartsWith("TextBox_Name")).Txt.Text,
memberNo = g.First(x => x.Txt.ID.StartsWith("TextBox_MemberNo")).Txt.Text,
points = int.Parse(g.First(x => x.Txt.ID.StartsWith("TextBox_Points")).Txt.Text)
})
.ToList();
}
Here's the method that creates the controls.
protected void createcontrols()
{
int count = 0;
if (ViewState["count"] != null)
{
count = (int)ViewState["count"];
}
while (PlaceHolder_ForEntries.Controls.Count < count)
{
TextBox TextBox_Name = new TextBox();
TextBox TextBox_MemberNo = new TextBox();
TextBox TextBox_Points = new TextBox();
DropDownList DropDownList_CarClass = new DropDownList();
DropDownList_CarClass.Items.Add("Car1");
...
DropDownList_CarClass.Items.Add("Car2");
TextBox_Name.Attributes.Add("placeholder", "Navn");
TextBox_Name.ID = "TextBox_Name" + PlaceHolder_ForEntries.Controls.Count.ToString();
TextBox_Name.CssClass = "input-small";
TextBox_MemberNo.Attributes.Add("placeholder", "Medlemsnr.");
TextBox_MemberNo.ID = "TextBox_MemberNo" + PlaceHolder_ForEntries.Controls.Count.ToString();
TextBox_MemberNo.CssClass = "input-small";
TextBox_Points.Attributes.Add("placeholder", "Point");
TextBox_Points.ID = "TextBox_Points" + PlaceHolder_ForEntries.Controls.Count.ToString();
TextBox_Points.CssClass = "input-small";
PlaceHolder_ForEntries.Controls.Add(TextBox_Name);
PlaceHolder_ForEntries.Controls.Add(TextBox_MemberNo);
PlaceHolder_ForEntries.Controls.Add(DropDownList_CarClass);
PlaceHolder_ForEntries.Controls.Add(TextBox_Points);
PlaceHolder_ForEntries.Controls.Add(new LiteralControl("<br />"));
}
}
you can use the Where and check if the instance of object is of type!
List<content> rows = PlaceHolder_ForEntries.Controls.Cast<Control>().Where(c => c is TextBox || c is DropDownList)
.Select(txt => new
{
Txt = txt,
Number = new String(txt.ID.SkipWhile(c => !Char.IsDigit(c)).ToArray())
})
.GroupBy(x => x.Number)
.Select(g => new content
{
carclass = g.First(x => x.Txt.ID.StartsWith("DropDownlist_CarClass")).Txt.SelectedValue,
name = g.First(x => x.Txt.ID.StartsWith("TextBox_Name")).Txt.Text,
memberNo = g.First(x => x.Txt.ID.StartsWith("TextBox_MemberNo")).Txt.Text,
points = int.Parse(g.First(x => x.Txt.ID.StartsWith("TextBox_Points")).Txt.Text)
})
.ToList();
AppDeveloper is right. OfType<T> filters out all objects of types other than T; so by filtering twice, you effectively eliminate all objects in the list.
If you wanted to wrap this logic (filtering all but two types from a list) into something reusable, nothing's stopping you from implementing your own extension method:
using System.Collections;
public static class EnumerableExtensions
{
public static IEnumerable OfType<T1, T2>(this IEnumerable source)
{
foreach (object item in source)
{
if (item is T1 || item is T2)
{
yield return item;
}
}
}
}
Including the above class in your project will allow you to write code like this in your application:
var textBoxesAndDropDowns = controls.OfType<TextBox, DropDownList>();
To learn more about extension methods, see the MSDN article on the subject.
Note that since the extension method above "lets in" two different types, the result is still a non-generic IEnumerable sequence. If you wanted to treat the result as a generic sequence (e.g., an IEnumerable<Control>), I would recommend using the Cast<T> extension method:
var filteredControls = controls.OfType<TextBox, DropDownList>().Cast<Control>();
I haven't read the question thoroughly, but from what the title implies, you can achieve the behavior with:
var collection = new object[] { 5, "4545", 'd', 54.5 , 576 };
var allowedTypes = new[] { typeof(string), typeof(int) };
var result = collection
.Where(item => allowedTypes.Contains(item.GetType()));
See it in action here.
Took #Shimmys answer for an extension method:
/// <param name="wantedTypes">--- Sample: --- new Type[] { typeof(Label), typeof(Button) }</param>
public static IEnumerable OfTypes(this IEnumerable collection, Type[] wantedTypes)
{
if (wantedTypes == null)
return null;
else
return collection.Cast<object>().Where(element => wantedTypes.Contains(element.GetType()));
}
Usage:
// List of 3 different controls
List<object> controls = new List<object>(new object[] { new Label(), new Button(), new TextBox() });
// Get all labels and buttons
var labelsAndButtons = controls.OfTypes(new Type[] { typeof(Label), typeof(Button) });
Your problem is in the OfType<>().OfType<>() you filter twice with different types
So I have this code building with no errors but I need to alter how its opening the xml documents. Right now it can open a single xml documents what I need it to do is open up a folder on my c: and parse through all the xml files in the folder. Any help?
static void Main(string[] args)
{
XDocument doc = XDocument.Load(#"c:\.cfg"); //Change here
var query = from x in doc.Descendants("X")
select new
{
Max1 = x.Attribute("Max").Value,
Min2 = x.Attribute("Min").Value
};
foreach (var x in query) ;
Console.WriteLine("X");
var query2 = from x in doc.Descendants("Y")
select new
{
Max3 = x.Attribute("Max").Value,
Min4 = x.Attribute("Min").Value
};
foreach (var x in query2)
Console.WriteLine("Y");
var query3 = from x in doc.Descendants("ZA")
select new
{
Max5 = x.Attribute("Max").Value,
Min6 = x.Attribute("Min").Value
};
foreach (var x in query3)
Console.WriteLine("Z");
}
You should loop through Directory.EnumerateFiles(#"C:\Something", "*.xml").
... A slightly more "declarative" manner:
// Program.cs
class Program
{
static void Main(string[] args)
{
const string path = #"C:\stuff";
Parallel.ForEach(Directory.EnumerateFiles(path, "*.xml"), x => Walk(XDocument.Load(x)));
}
static IEnumerable<Calib> MapItem(IEnumerable<XElement> elements)
{
return elements.Select(x => new Calib
{
Max = x.Attribute("Max").Value,
Min = x.Attribute("Min").Value
});
}
static void Walk(XDocument doc)
{
var xitems = MapItem(doc.Descendants("XaxisCalib"));
xitems.Iter(x => Console.WriteLine("(XaxisCalib) X: Min = {0} | Max = {1}", x.Min, x.Max));
var yitems = MapItem(doc.Descendants("YAxisCalib"));
yitems.Iter(x => Console.WriteLine("(YaxisCalib) Y: Min = {0} | Max = {1}", x.Min, x.Max));
var zitems = MapItem(doc.Descendants("ZAxisCalib"));
zitems.Iter(x => Console.WriteLine("(ZaxisCalib) Z: Min = {0} | Max = {1}", x.Min, x.Max));
}
}
// Exts.cs
public static class Exts
{
public static void Iter<T>(this IEnumerable<T> source, Action<T> action)
{
foreach (var item in source)
{
action(item);
}
}
}
// Calib.cs
public class Calib
{
public string Max { get; set; }
public string Min { get; set; }
}
Rather than just writing the values out to the console, you could create a new Xml document from the values in the files and do whatever you want with from that (generate an Excel spreadsheet?):
var fileData = new XElement("root",
from file in New System.IO.DirectoryInfo("C:\Something").GetFiles()
where file.Extension.Equals(".xml", String Comparison.CurrentCultureIgnoreCase)
Let doc = XElement.Load(file.FullName)
select new XElement("File",
new XAttribute("Path", file.FullName),
select new XElement("XAxisCalibs",
from x in doc.Descendants("XAxisCalib")
select new XElement("XAxisCalib",
new XAttribute("Max", x.Attribute("Max").Value),
new XAttribute("Min", x.Attribute("Min").Value)
)
),
select new XElement("YAxisCalibs",
from y in doc.Descendants("YAxisCalib")
select new XElement("YAxisCalib",
new XAttribute("Max", x.Attribute("Max").Value),
new XAttribute("Min", x.Attribute("Min").Value)
)
),
select new XElement("ZAxisCalibs",
from z in doc.Descendants("ZAxisCalib")
select new XElement("ZAxisCalib",
new XAttribute("Max", x.Attribute("Max").Value),
new XAttribute("Min", x.Attribute("Min").Value)
)
)
);
Granted, since this is complete declarative and one long statement, it is a bit of a trick to debug if necessary.