Pairwise matching and windowing using Deedle in C# - c#

Say we want to calculate how the price of some fruits are changing. Starting from a CSV file:
Day,Name,Kind,Price
2019-09-04,"apple","red delicious",63.09
2019-09-04,"apple","ginger crisp",52.14
2019-09-04,"orange","navel",41.18
2019-09-03,"apple","red delicious",63.07
2019-09-03,"apple","ginger crisp",52.11
2019-09-03,"orange","navel",41.13
2019-09-02,"apple","red delicious",63.00
2019-09-02,"apple","ginger crisp",52.00
2019-09-02,"orange","navel",41.00
with an unknown number of fruits and varieties, we can read the dataframe and build an extra column to use for matching.
var fruits_file = Path.Combine(root, "fruits.csv");
Deedle.Frame<int, string> df = Frame.ReadCsv(fruits_file);
Series<int, string> name = df.GetColumn<string>("Name");
Series<int, string> kind = df.GetColumn<string>("Kind");
var namekind = name.ZipInner(kind).Select(t => t.Value.Item1 + t.Value.Item2);
df.AddColumn("NameKind", namekind);
but the problem remains. Deedle.Series.Window() and Deedle.Series.Pairwise() make it possible to perform first-order differences, but not matching based on some string (namekind).
What is the right way to copy over a column LastPrice and subsequently calculate the Change?
Day,Name,Kind,Price,LastPrice,Change
2019-09-04,"apple","red delicious",63.09,63.07,0.02
2019-09-04,"apple","ginger crisp",52.14,52.11,0.03
2019-09-04,"orange","navel",41.18,41.13,0.05
2019-09-03,"apple","red delicious",63.07,63.00,0.07
2019-09-03,"apple","ginger crisp",52.11,52.00,0.11
2019-09-03,"orange","navel",41.13,41.00,0.13
2019-09-02,"apple","red delicious",63.00,,
2019-09-02,"apple","ginger crisp",52.00,,
2019-09-02,"orange","navel",41.00,,

See code below :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.IO;
namespace ConsoleApplication137
{
class Program
{
const string FILENAME = #"c:\temp\test.csv";
static void Main(string[] args)
{
Fruit fruit = new Fruit(FILENAME);
Fruit.PrintFruits();
Console.ReadLine();
}
}
public class Fruit
{
public static List<Fruit> fruits { get; set; }
public DateTime day { get; set; }
public string name { get; set; }
public string kind { get; set; }
public decimal price { get; set; }
public Fruit() { }
public Fruit(string filename)
{
int count = 0;
StreamReader reader = new StreamReader(filename);
string line = "";
while ((line = reader.ReadLine()) != null)
{
if (++count > 1)
{
string[] splitLine = line.Split(new char[] { ',' }).ToArray();
Fruit newFruit = new Fruit();
if (fruits == null) fruits = new List<Fruit>();
fruits.Add(newFruit);
newFruit.day = DateTime.Parse(splitLine[0]);
newFruit.name = splitLine[1];
newFruit.kind = splitLine[2];
newFruit.price = decimal.Parse(splitLine[3]);
}
}
reader.Close();
}
public static void PrintFruits()
{
var groups = fruits.OrderBy(x => x.day)
.GroupBy(x => new { name = x.name, kind = x.kind })
.ToList();
foreach (var group in groups)
{
for (int i = 0; i < group.Count() - 1; i++)
{
Console.WriteLine("Old Date : '{0}', New Date : '{1}', Name : '{2}', Kind : '{3}', Old Price '{4}', New Price '{5}', Delta Price '{6}'",
group.ToList()[i].day.ToString("yyyy-MM-dd"),
group.ToList()[i + 1].day.ToString("yyyy-MM-dd"),
group.ToList()[i].name,
group.ToList()[i].kind,
group.ToList()[i].price.ToString(),
group.ToList()[i + 1].price.ToString(),
(group.ToList()[i + 1].price - group.ToList()[i].price).ToString()
);
}
}
}
}
}

Related

How to write a arrayList into a array?

I am trying to get the count of an arrayList numbers which represent days(which are in a List) written into a another array so I can sort it.
Array list:
2 3 4 5 6 7
1 2 3 4
1 2 3
5 6 7
1 2 3
1 2 3 4 5 6 7
In this case I can print the count of each ArrayList, I just can't work with it. I wish to sort it so I can get two biggest numbers from it.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO;
using System.Collections;
namespace Laboratorinis_P3
{
public partial class Form1 : Form
{
List<Museum> firstList;
List<Museum> newList;
List<Museum> twoList;
List<Museum> TTList;
public Form1()
{
InitializeComponent();
}
private void formListBySelectedCityToolStripMenuItem_Click(object sender, EventArgs e)
{
string city = Convert.ToString(Cities.SelectedItem);
LinqForming(city);
}
private void findTwoMuseumsToolStripMenuItem_Click(object sender, EventArgs e)
{
FindTwoMuseums(firstList);
}
private void readFileAndPrintItToolStripMenuItem_Click(object sender, EventArgs e)
{
OpenFileDialog openFileDialog1 = new OpenFileDialog();
openFileDialog1.Filter = "txt files (*.txt)|*.txt|All files (*.*)|*.*";
openFileDialog1.Title = "Pasirinkite duomenų failą";
DialogResult result = openFileDialog1.ShowDialog();
if (result == DialogResult.OK)
{
string path = openFileDialog1.FileName;
firstList = ReadFile(path,firstList);
PrintList(firstList,"Read file");
AddCitiesToComboBox(firstList);
}
}
static List<Museum> ReadFile(string fv,List<Museum> firstList)
{
firstList = new List<Museum>();
using (StreamReader reader = new StreamReader(fv))
{
string line;
while ((line = reader.ReadLine()) != null)
{
ArrayList days = new ArrayList();
string[] parts = line.Split(";");
string name = parts[0];
string city = parts[1];
string type = parts[2];
string[] day = parts[3].Trim().Split(new[] {' '});
days.Clear();
foreach (string lines in day)
{
int dayss = int.Parse(lines);
days.Add(dayss);
}
double adult = double.Parse(parts[4]);
double kid = double.Parse(parts[5]);
string hasguide = parts[6];
Museums museum = new Museums(name, city, type, days, adult, kid, hasguide);
firstList.Add(museum);
}
}
return firstList;
}
private void LinqForming(string city)
{
newList = firstList
.Where(x => x.City == city)
.ToList();
}
private void AddCitiesToComboBox(List<Museum> list)
{
for (int i = 0; i < list.Count; i++)
{
if (!Cities.Items.Contains(list[i].City))
{
Cities.Items.Add(list[i].City);
}
}
}
private void FindTwoMuseums(List<Museum> museum)
{
}
private void PrintList(List<Museum> museum, string info)
{
finalResults.Text += info + "\n";
string top =
"|------|---------|---------|-------------|--------|------|-----------|\n"
+ "| Name | City | Type | Days | Adult | Kid | Has guide |\n"
+ "|------|---------|---------|-------------|--------|------|---------- |\n";
finalResults.Text += top;
for (int i = 0; i < museum.Count; i++)
{
finalResults.Text += museum[i] + "\n";
}
}
}
}
namespace Laboratorinis_P3
{
class Museum
{
public string Name { get; set; }
public string City { get; set; }
public string MuseumTypes { get; set; }
public ArrayList Days { get; set; }
public double PriceForAdult { get; set; }
public double PriceForKid { get; set; }
public string HasGuide { get; set; }
public Museum()
{
}
public Museum(string name, string city, string type, ArrayList days,
double adult, double kid, string hasguide)
{
this.Name = name;
this.City = city;
this.MuseumTypes = type;
this.Days = days;
this.PriceForAdult = adult;
this.PriceForKid = kid;
this.HasGuide = hasguide;
}
public override string ToString()
{
string eilute;
eilute = string.Format
("|{0,6}|{1,9}|{2,9}|{3,13}|{4,8}|{5,6}|{6,11}|", Name, City, MuseumTypes,
String.Join(" ", Days.ToArray().Select(s => s.ToString())), PriceForAdult, PriceForKid, HasGuide);
return eilute;
}
}
}
If you want to return the two Museums with the highest Days property, you can do:
using System.Linq;
List<Museum> FindTwoMuseums(List<Museum> list)
{
return list.OrderByDescending(x => x.Days).Take(2).ToList();
}
You could dump all the values into a single list using something like this:
List<int> allDays = new List<int>;
for (int i = 0; i < museum.Count; i++)
{
foreach (int day in museum[i].Days)
{
allDays.Add(day);
}
}
Then you could just sort that list and easily find your two largest ints.
If you have a list of list, use select many to flatten it
https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.selectmany?redirectedfrom=MSDN#overloads

Order lines from a text file by number found in a certain spot of the line

I'm sorry that I interrupt you in this manner, I'm new to C# and I've been struggling with this problem for days... Maybe it will seem easy for you :)
I have this text file in this format
name|ID|domain|grade|verdict
Ryan|502322|Computers|9,33|Undefined
Marcel|302112|Automatics|6,22|Undefined
Alex|301234|Computers|5,66|Undefined
Leo|201122|Automatics|3,22|Undefined
How can I sort the text file using any methods (including LINQ) so that the list from the text file will be ordered by domain, and then descending by the grade column? Like this:
name|ID|domain|grade|verdict
Marcel|302112|Automatics|6,22|Undefined
Leo|201122|Automatics|3,22|Undefined
Ryan|502322|Computers|9,33|Undefined
Alex|301234|Computers|5,66|Undefined
To read the file, I'm using var Students = File.ReadAllLines(#"filepath");, I don't know if it's the smartest approach, and then I write using File.WriteAllLines
Thanks in advance! Sorry once again, I know it should be easy, but for me is really tuff :(
You can use some thing like this:
var students= File.ReadAllLines(#"filepath");
var headers = lines[0];
students = lines.Skip(1).ToArray();
var orders = lines.Select(x => x.Split('|'))
.Select(x => new { Domain = x[2], Grade = int.Parse(x[3].Replace(",", "")), All = x })
.OrderBy(x => x.Domain).ThenByDescending(x => x.Grade).Select(x => string.Join("|", x.All)).ToList();
orders.Insert(0, headers);
students=orders.ToArray();
try following code:
private void ReadFile()
{
char Delimiter = '|';
string[] Lines = File.ReadAllLines(#"E:\RaftehHa.txt", Encoding.Default);
List<string[]> FileRows = Lines.Select(line =>
line.Split(new[] { Delimiter }, StringSplitOptions.RemoveEmptyEntries)).ToList();
DataTable dt = new DataTable();
dt.Columns.AddRange(FileRows[0].Select(col => new DataColumn() { ColumnName = col }).ToArray());
FileRows.RemoveAt(0);
FileRows.ForEach(row => dt.Rows.Add(row));
DataView dv = dt.DefaultView;
dv.Sort = " ID ASC ";
dt = dv.ToTable();
dataGridView1.DataSource = dt;
}
A bit the same as already mentioned above, but as you mention you are new to C#, I have tried to add a little bit of structure to the code, but leaving the completion to you.
public class Data
{
public Data(string inputLine)
{
var split = inputLine.Split('|');
Name = split[0];
Id = int.Parse(split[1]);
Domain = split[2];
Grade = double.Parse(split[3].Replace(",", "."));
Verdict = split[4];
}
public string Name { get; }
public int Id { get; }
public string Domain { get; }
public double Grade { get; }
public string Verdict { get; }
}
public class DataFile
{
public static IEnumerable Read(string fileName)
{
var input = File.ReadAllLines(fileName);
return input.Skip(1).Select(p => new Data(p)); // skip header
}
public static void Write(IEnumerable data)
{
// todo :)
}
}
void Main()
{
var input = DataFile.Read(#"C:\Temp\ExampleData.txt");
var result = input.OrderBy(p => p.Domain).ThenByDescending(p => p.Grade);
DataFile.Write(result);
}
using System;
using System.IO;
using System.Linq;
using System.Collections.Generic;
enum DomainType {
Automatics, // 0
Computers // 1
}
class Data {
public int Id { get; set; }
public string Name { get; set; }
public string Verdict { get; set; }
public DomainType Domain { get; set; }
public Tuple<int, int> Grade { get; set; }
}
public static class Program {
static IEnumerable<Data> FileContent(string path) {
string line;
using (var reader = File.OpenText(path))
{
bool skipHeader = false;
while((line = reader.ReadLine()) != null)
{
if (!skipHeader) {
skipHeader = true;
continue;
}
var fields = line.Split('|');
string name = fields[0];
int id = int.Parse(fields[1]);
var domain = (DomainType)Enum.Parse(typeof(DomainType), fields[2]);
var grade = Tuple.Create(int.Parse(fields[3].Split(',')[0]),
int.Parse(fields[3].Split(',')[1]));
string verdict = fields[4];
var data = new Data() {
Name = name, Id = id, Domain = domain, Grade = grade, Verdict = verdict };
yield return data;
}
}
}
public static void Main() {
var result = FileContent("path_to_file").OrderBy(data => data.Domain);
foreach (var line in result) {
Console.WriteLine(line.Name);
}
}
}

CSV with headers to XML in C#

I'm trying to convert a csv to a xml.
CSV sample
ID;name;addresses;street;streetNR
1;peter;;streetTest;58784
1;peter;;street2;04512
this should look like
<ID>
<name>Peter</name>
<addresses>
<address>
<street>streetTest</street>
<plz>58784</plz>
</address>
<address>
<street>street2</street>
<plz>04512</plz>
</address>
</address>
</ID>
The file XML is huge. It's around 100 tags. What is the smartest way to do this? I already checked the forum but can't find something that fits well enough.
You can use XElement and Linq to convert the csv with header to xml
var header = File.ReadLines(inputfile).First().Split(';'); //Read header
var lines = File.ReadAllLines(inputfile).Skip(1); //Skip header row
//format the xml how you want
var xml = new XElement("EmpDetails",
lines.Select(line => new XElement("Item",
line.Split(';').Select((column, index) => new XElement(header[index], column)))));
xml.Save(outputfile); //output xml file
Try following xml linq :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.IO;
namespace ConsoleApplication1
{
class Program
{
const string INPUT_FILENAME = #"c:\temp\test.txt";
const string OUTPUT_FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Parse("<root></root>");
XElement root = doc.Root;
StreamReader reader = new StreamReader(INPUT_FILENAME);
int rowCount = 0;
string[] headers = null;
string line = "";
List<KeyValuePair<string, string[]>> data = new List<KeyValuePair<string, string[]>>(); ;
while ((line = reader.ReadLine()) != null)
{
if (++rowCount == 1)
{
headers = line.Split(new char[] { ';' }).ToArray();
}
else
{
string[] splitArray = line.Split(new char[] { ';' }).ToArray();
data.Add(new KeyValuePair<string,string[]>(splitArray[0], splitArray));
}
}
var groups = data.GroupBy(x => x.Key).ToList();
foreach (var group in groups)
{
int groupCount = 0;
XElement addresses = null;
foreach (var row in group)
{
string[] colData = row.Value;
if (++groupCount == 1)
{
XElement xGroup = new XElement("ID", new object[] {
new XElement("name", colData[1]),
new XElement("addresses")
});
root.Add(xGroup);
addresses = xGroup.Element("addresses");
}
int min = Math.Min(headers.Length, colData.Length);
XElement address = new XElement("address");
addresses.Add(address);
for (int i = 3; i < min; i++)
{
XElement xCol = new XElement(headers[i], colData[i]);
address.Add(xCol);
}
}
}
doc.Save(OUTPUT_FILENAME);
}
}
public class Material
{
public string name { get; set; }
public string className { get; set; }
public List<Property> properties { get; set; }
}
public class Property
{
public string property { get; set; }
public string format { get; set; }
public string value { get; set; }
}
}

How to parse INI file?

I have ini files that contains data from server.
Map.ini
[MAP_1]
MapType = 1
MapWar = 1
Position = 42.03,738.2,737.3
[MAP_2]
MapType = 1
MapWar = 1
Position = 42.03,738.2,737.3
How to read map.ini that contains this kind of files and save it in Dictionary or List.
Try following :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.txt";
static void Main(string[] args)
{
List<Map> maps = new List<Map>();
Map map = null;
StreamReader reader = new StreamReader(FILENAME);
string line = "";
while ((line = reader.ReadLine()) != null)
{
line.Trim();
if (line.Length > 0)
{
if(line.StartsWith("["))
{
string name = line.Trim(new char[] { '[', ']' });
map = new Map();
maps.Add(map);
map.name = name;
}
else
{
string[] data = line.Split(new char[] { '=' });
switch (data[0].Trim())
{
case "MapType" :
map.mapType = int.Parse(data[1]);
break;
case "MapWar":
map.mapWar = int.Parse(data[1]);
break;
case "Position":
string[] numbers = data[1].Split(new char[] { ',' });
map.x = decimal.Parse(numbers[0]);
map.y = decimal.Parse(numbers[1]);
map.z = decimal.Parse(numbers[2]);
break;
default :
break;
}
}
}
}
Dictionary<string, Map> dict = maps
.GroupBy(x => x.name, y => y)
.ToDictionary(x => x.Key, y => y.FirstOrDefault());
}
}
public class Map
{
public string name { get; set; }
public int mapType { get; set; }
public int mapWar { get; set; }
public decimal x { get; set; }
public decimal y { get; set; }
public decimal z { get; set; }
}
}

Composite join on two CSV files in C#

Starting from a table of daily fruit prices
fruits.csv
Day,Name,Kind,Price
2019-09-04,"apple","red",63.09
2019-09-04,"apple","yellow",52.14
2019-09-04,"orange","navel",41.18
2019-09-04,"orange","blood",41.18
2019-09-03,"apple","red",63.07
2019-09-03,"apple","yellow",52.11
2019-09-03,"orange","navel",41.13
2019-09-03,"orange","blood",41.13
I'd like to insert the reference prices by name and kind
fruit_ref_prices.csv
Name,Kind,Reference_Price
"apple","red",60.00
"apple","yellow",50.00
"orange","navel",40.00
"orange","blood",42.00
to result in the following table
Day,Name,Kind,Price,Reference_Price
2019-09-04,"apple","red",63.09,60.00
2019-09-04,"apple","yellow",52.14,50.00
2019-09-04,"orange","navel",41.18,40.00
2019-09-04,"orange","blood",41.18,42.00
2019-09-03,"apple","red",63.07,60.00
2019-09-03,"apple","yellow",52.11,50.00
2019-09-03,"orange","navel",41.13,40.00
2019-09-03,"orange","blood",41.13,42.00
The solution should be simple using C#'s built-in SQL-like syntax, and I'm sure the answer lies in one of the following tutorial pages:
Join clause
Perform custom join operations
Join by using composite keys
but I'm having a hard time identifying the syntax of this language.
In my attempt below instead of writing
join fruit_ref in fruit_refs on fruit.name equals fruit_ref.name
I should be able to write
join fruit_ref in fruit_refs on fruit.name equals fruit_ref.name
and fruit.kind equals fruit_ref.kind
but the Boolean expression is not accepted. Why?
My attempt is:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.IO;
namespace MyConsoleApplication
{
class Program
{
const string root = #"c:\path\to\here\";
const string file1_in = root + #"fruits.csv";
const string file2_in = root + #"fruit_ref_prices.csv";
static void Main(string[] args)
{
Fruit_Basket fruit_basket = new Fruit_Basket(file1_in, file2_in);
fruit_basket.PrintFruits();
}
}
public class Fruit
{
public DateTime day { get; set; }
public string name { get; set; }
public string kind { get; set; }
public decimal price { get; set; }
public Fruit(DateTime newFruit_day,
string newFruit_name,
string newFruit_kind,
decimal newFruit_price)
{
this.day = newFruit_day;
this.name = newFruit_name;
this.kind = newFruit_kind;
this.price = newFruit_price;
}
}
public class Fruit_Ref
{
public string name;
public string kind;
public decimal reference_price;
public Fruit_Ref(string newName, string newKind, decimal newRef_Price)
{
this.name = newName;
this.kind = newKind;
this.reference_price = newRef_Price;
}
}
public class Fruit_Basket {
public List<Fruit> fruits { get; set; }
public List<Fruit_Ref> fruit_refs { get; set; }
public Fruit_Basket(string file1_in, string file2_in) {
build_fruit_list(file1_in);
build_fruit_ref_list(file2_in);
}
public void build_fruit_list(string file_in)
{
fruits = new List<Fruit>();
int count = 0;
StreamReader reader = new StreamReader(file_in);
string line = "";
while ((line = reader.ReadLine()) != null)
{
if (++count > 1)
{
string[] splitLine = line.Split(new char[] { ',' }).ToArray();
var newFruit_day = DateTime.Parse(splitLine[0]);
var newFruit_name = splitLine[1];
var newFruit_kind = splitLine[2];
var newFruit_price = decimal.Parse(splitLine[3]);
Fruit newFruit = new Fruit(newFruit_day,
newFruit_name,
newFruit_kind,
newFruit_price);
fruits.Add(newFruit);
}
}
reader.Close();
}
public void build_fruit_ref_list(string file_in)
{
fruit_refs = new List<Fruit_Ref>();
int count = 0;
StreamReader reader = new StreamReader(file_in);
string line = "";
while ((line = reader.ReadLine()) != null)
{
if (++count > 1)
{
string[] splitLine = line.Split(new char[] { ',' }).ToArray();
var newFruit_name = splitLine[0];
var newFruit_kind = splitLine[1];
var newFruit_ref_price = decimal.Parse(splitLine[2]);
Fruit_Ref newFruit_ref = new Fruit_Ref(newFruit_name,
newFruit_kind,
newFruit_ref_price);
fruit_refs.Add(newFruit_ref);
}
}
reader.Close();
}
public void PrintFruits()
{
var innerJoinQuery =
from fruit in fruits
join fruit_ref in fruit_refs on fruit.name equals fruit_ref.name
select new { Day = fruit.day, Name = fruit.name, Kind = fruit.kind,
Price = fruit.price, Reference_Price = fruit_ref.reference_price };
Console.WriteLine($#"""Date"",""Name"",""Kind"",""Price"",""Ref Price""");
foreach (var i in innerJoinQuery)
{
Console.WriteLine($#"{i.Day},{i.Kind},{i.Price},{i.Reference_Price}");
}
}
}
}
You could also refactor your code to use the CsvHelper NuGet package for reading/writing CSV files.
First, You can make these classes to reflect the fruits, fruit references and final fruit structure.
public class Fruit
{
public string Day { get; set; }
public string Name { get; set; }
public string Kind { get; set; }
public string Price { get; set; }
}
public class FruitReferencePrice
{
public string Name { get; set; }
public string Kind { get; set; }
public string Reference_Price { get; set; }
}
public class FruitFinal
{
public string Day { get; set; }
public string Name { get; set; }
public string Kind { get; set; }
public string Price { get; set; }
public string ReferencePrice { get; set; }
public override string ToString()
{
return $"Day={Day},Name={Name},Kind={Kind},Price={Price},Reference_Price={ReferencePrice}";
}
}
Then you can make two methods to return the rows of each CSV file into List<Fruit> and List<FruitReferencePrice>.
private static IEnumerable<Fruit> BuildFruitList(string csvFilePath)
{
if (!File.Exists(csvFilePath))
{
throw new FileNotFoundException("Could not locate CSV at path " + csvFilePath, csvFilePath);
}
try
{
using var fileReader = File.OpenText(csvFilePath);
using var csv = new CsvReader(fileReader);
return csv.GetRecords<Fruit>().ToList();
} catch (Exception ex)
{
Console.WriteLine(ex.Message);
return Enumerable.Empty<Fruit>();
}
}
private static IEnumerable<FruitReferencePrice> BuildFruitReferenceList(string csvFilePath)
{
if (!File.Exists(csvFilePath))
{
throw new FileNotFoundException("Could not locate CSV at path " + csvFilePath, csvFilePath);
}
try
{
using var fileReader = File.OpenText(csvFilePath);
using var csv = new CsvReader(fileReader);
return csv.GetRecords<FruitReferencePrice>().ToList();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
return Enumerable.Empty<FruitReferencePrice>();
}
}
Then you can perform a grouped join and output the merged result.
var path1 = "PATH\\fruits.csv";
var path2 = "PATH\\fruit_ref_prices.csv";
var fruitList = BuildFruitList(path1);
var fruitReferencePrices = BuildFruitReferenceList(path2);
var groupedJoin = from fruit in fruitList
join fruit_ref in fruitReferencePrices
on new { fruit.Name, fruit.Kind } equals new { fruit_ref.Name, fruit_ref.Kind }
select new FruitFinal
{
Day = fruit.Day,
Name = fruit.Name,
Kind = fruit.Kind,
Price = fruit.Price,
ReferencePrice = fruit_ref.Reference_Price
};
foreach (var fruit in groupedJoin)
{
Console.WriteLine(fruit.ToString());
}
Merged results:
Day=2019-09-04,Name=apple,Kind=red,Price=63.09,Reference_Price=60.00
Day=2019-09-04,Name=apple,Kind=yellow,Price=52.14,Reference_Price=50.00
Day=2019-09-04,Name=orange,Kind=navel,Price=41.18,Reference_Price=40.00
Day=2019-09-04,Name=orange,Kind=blood,Price=41.18,Reference_Price=42.00
Day=2019-09-03,Name=apple,Kind=red,Price=63.07,Reference_Price=60.00
Day=2019-09-03,Name=apple,Kind=yellow,Price=52.11,Reference_Price=50.00
Day=2019-09-03,Name=orange,Kind=navel,Price=41.13,Reference_Price=40.00
Day=2019-09-03,Name=orange,Kind=blood,Price=41.13,Reference_Price=42.00
Please change the equals clause as on new { fruit.name, fruit.kind } equals new { fruit_ref.name, fruit_ref.kind }
Why you require this
The query has two anonymous types (one for left table and one for right table). So to compare those anonymous types, the linq statement should use new keyword
Query :
var innerJoinQuery = from fruit in fruits
join fruit_ref in fruit_refs on new { fruit.name, fruit.kind } equals new { fruit_ref.name, fruit_ref.kind }
select new { Day = fruit.day, Name = fruit.name, Kind = fruit.kind,
Price = fruit.price, Reference_Price = fruit_ref.reference_price };

Categories