foreach with multiple DictionaryEntry - c#

I am trying to write to a ListView using the contents of 3 three already existing resx files. Using the following loop with only one of the files yields close to what I want but I need is to use the same loop with multiple DictionaryEntrys. What I am trying to do looks like this..
ResXResourceReader rdr0 = new ResXResourceReader(textPath1.Text + ".resx");
ResXResourceReader rdr1 = new ResXResourceReader(textPath1.Text + ".es.resx");
ResXResourceReader rdr2 = new ResXResourceReader(textPath1.Text + ".fr.resx");
foreach ((DictionaryEntry d in rdr0) && (DictionaryEntry e in rdr1))
{
string[] row = { d.Key.ToString(), d.Value.ToString(), e.Value.ToString() };
var listViewItem = new ListViewItem(row);
listResx.Items.Add(listViewItem);
}

The foreach keyword cannot do that.
Instead, you can use the LINQ .Zip() method:
foreach(var item in rdr0.Zip(rdr1, (d, e)
=> new [] { d.Key.ToString(), d.Value.ToString(), e.Value.ToString() }))

Related

Compare two font collections

I'm trying to compare two font collections in order to understand which fonts are already installed and which not.
Code is as follows:
var workingdir = new DirectoryInfo(Path.Combine(basepath, directory));
InstalledFontCollection col = new InstalledFontCollection();
PrivateFontCollection pcol = new PrivateFontCollection();
foreach (FileInfo fontname in workingdir.GetFiles("*.ttf"))
{
pcol.AddFontFile(fontname.FullName);
}
foreach (var item in pcol.Families)
{
if (col.Families.Contains(item))
{
Console.WriteLine(item.Name + " already installed");
}
else
{
Console.WriteLine(item.Name + " NOT INSTALLED");
}
}
Problem is that I know for sure that inside my workingdir there are some fonts already installed and some not, but the console output shows me that EVERY fontfile is not installed.
What am I missing? I guess there's something wrong in my logic but I don't understand where is the problem...
Contains checks if it is the same object with ==, but you have to check for the Names to be the same.
var workingdir = new DirectoryInfo(Path.Combine(basepath, directory));
var col = new InstalledFontCollection();
var pcol = new PrivateFontCollection();
foreach (var fontname in workingdir.GetFiles("*.ttf"))
{
pcol.AddFontFile(fontname.FullName);
}
foreach(var item in pcol.Families.Where(a => col.Families.Any(b => b.Name == a.Name)))
{
Console.WriteLine($"'{item.Name}' already installed");
}

Modify CSV file headers/column names using Cinchoo ETL

I have a .Net Core application where I want to change the column names of a csv file. I'm using the Cinchoo ETL library. I have tried the following:
string csv = "../../../../data.csv";
using (var w = new ChoCSVWriter(csv).WithFirstLineHeader().Setup(s => s.FileHeaderWrite += (o, e) =>
{
e.HeaderText = "Test,Test2";
}))
{
w.Write(csv);
}
This is what my data.csv file looks like:
ID,Name
1, David
2, Bob
This is what my csv looks like after running my code:
Test,Test2
../../../../data.csv
The csv header names have changed but my issue is that it deleted all my data and added the path to the file for some odd reason. Any ideas on why that is?
Couple of ways you can rename the columns with new names and produce the CSV output
Option1:
StringBuilder csvIn = new StringBuilder(#"ID,Name
1, David
2, Bob");
StringBuilder csvOut = new StringBuilder();
using (var r = new ChoCSVReader(csvIn)
.WithFirstLineHeader()
)
{
using (var w = new ChoCSVWriter(csvOut)
.WithFirstLineHeader()
)
w.Write(r.Select(r1 => new { Test1 = r1.ID, Test2 = r1.Name }));
}
Console.WriteLine(csvOut.ToString());
Option2:
StringBuilder csvIn = new StringBuilder(#"ID,Name
1, David
2, Bob");
StringBuilder csvOut = new StringBuilder();
using (var r = new ChoCSVReader(csvIn)
.WithFirstLineHeader()
)
{
using (var w = new ChoCSVWriter(csvOut)
.WithFirstLineHeader()
.Setup(s => s.FileHeaderWrite += (o, e) =>
{
e.HeaderText = "Test,Test2";
})
)
w.Write(r);
}
Console.WriteLine(csvOut.ToString());
UPDATE:
Using CSV files instead of text input
string csvInFilePath = #"C:\CSVIn.csv"
string csvOutFilePath = #"C:\CSVOut.csv"
using (var r = new ChoCSVReader(csvInFilePath)
.WithFirstLineHeader()
)
{
using (var w = new ChoCSVWriter(csvOutFilePath)
.WithFirstLineHeader()
)
w.Write(r.Select(r1 => new { Test1 = r1.ID, Test2 = r1.Name }));
}
UPDATE:
To get the headers, cast record to IDictionary and use Keys property on it to get the keys
string csvInFilePath = #"C:\CSVIn.csv"
string csvOutFilePath = #"C:\CSVOut.csv"
using (var r = new ChoCSVReader(csvInFilePath)
.WithFirstLineHeader()
)
{
foreach (IDictionary<string, object> rec in r)
{
var keys = rec.Keys.ToArray();
}
}
In order to auto discover the datatypes of CSV columns, you must set the MaxScanRows on parser. Otherwise all columns will be treated as string type.
StringBuilder csvIn = new StringBuilder(#"ID,Name,Date
1, David, 1/1/2018
2, Bob, 2/12/2019");
using (var r = new ChoCSVReader(csvIn)
.WithFirstLineHeader()
.WithMaxScanRows(2)
)
{
foreach (IDictionary<string, object> rec in r.Take(1))
{
foreach (var kvp in rec)
Console.WriteLine($"{kvp.Key} - {r.Configuration[kvp.Key].FieldType}");
}
}
Hope it helps.

Find new file in two folders with a cross check

I am trying to sort two folders in to a patched folder, finding which file is new in the new folder and marking it as new, so i can transfer that file only. i dont care about dates or hash changes. just what file is in the new folder that is not in the old folder.
somehow the line
pf.NFile = !( oldPatch.FindAll(s => s.Equals(f)).Count() == 0);
is always returning false. is there something wrong with my logic of cross checking?
List<string> newPatch = DirectorySearch(_newFolder);
List<string> oldPatch = DirectorySearch(_oldFolder);
foreach (string f in newPatch)
{
string filename = Path.GetFileName(f);
string Dir = (Path.GetDirectoryName(f).Replace(_newFolder, "") + #"\");
PatchFile pf = new PatchFile();
pf.Dir = Dir;
pf.FName = filename;
pf.NFile = !( oldPatch.FindAll(s => s.Equals(f)).Count() == 0);
nPatch.Files.Add(pf);
}
foreach (string f in oldPatch)
{
string filename = Path.GetFileName(f);
string Dir = (Path.GetDirectoryName(f).Replace(_oldFolder, "") + #"\");
PatchFile pf = new PatchFile();
pf.Dir = Dir;
pf.FName = filename;
if (!nPatch.Files.Exists(item => item.Dir == pf.Dir &&
item.FName == pf.FName))
{
nPatch.removeFiles.Add(pf);
}
}
I don't have the classes you are using (like DirectorySearch and PatchFile), so i can't compile your code, but IMO the line _oldPatch.FindAll(... doesn't return anything because you are comparing the full path (c:\oldpatch\filea.txt is not c:\newpatch\filea.txt) and not the file name only. IMO your algorithm could be simplified, something like this pseudocode (using List.Contains instead of List.FindAll):
var _newFolder = "d:\\temp\\xml\\b";
var _oldFolder = "d:\\temp\\xml\\a";
List<FileInfo> missing = new List<FileInfo>();
List<FileInfo> nPatch = new List<FileInfo>();
List<FileInfo> newPatch = new DirectoryInfo(_newFolder).GetFiles().ToList();
List<FileInfo> oldPatch = new DirectoryInfo(_oldFolder).GetFiles().ToList();
// take all files in new patch
foreach (var f in newPatch)
{
nPatch.Add(f);
}
// search for hits in old patch
foreach (var f in oldPatch)
{
if (!nPatch.Select (p => p.Name.ToLower()).Contains(f.Name.ToLower()))
{
missing.Add(f);
}
}
// new files are in missing
One possible solution with less code would be to select the file names, put them into a list an use the predefined List.Except or if needed List.Intersect methods. This way a solution to which file is in A but not in B could be solved fast like this:
var locationA = "d:\\temp\\xml\\a";
var locationB = "d:\\temp\\xml\\b";
// takes file names from A and B and put them into lists
var filesInA = new DirectoryInfo(locationA).GetFiles().Select (n => n.Name).ToList();
var filesInB = new DirectoryInfo(locationB).GetFiles().Select (n => n.Name).ToList();
// Except retrieves all files that are in A but not in B
foreach (var file in filesInA.Except(filesInB).ToList())
{
Console.WriteLine(file);
}
I have 1.xml, 2.xml, 3.xml in A and 1.xml, 3.xml in B. The output is 2.xml - missing in B.

querying existing ListView items with LINQ

The ListView I have populates through these loops resulting in four columns being filled
// Create a ResXResourceReader
ResXResourceReader rdr0 = new ResXResourceReader(textPath1.Text + ".resx");
ResXResourceReader rdr1 = new ResXResourceReader(textPath1.Text + ".es.resx");
ResXResourceReader rdr2 = new ResXResourceReader(textPath1.Text + ".fr.resx");
foreach (DictionaryEntry d in rdr0)
{
TransResource x = new TransResource();
x.id = d.Key.ToString();
x.en = d.Value.ToString();
resources.Add(x.id, x);
}
foreach (DictionaryEntry d in rdr1)
{
TransResource x = resources[d.Key.ToString()];
x.fr = d.Value.ToString();
}
foreach (DictionaryEntry d in rdr2)
{
TransResource x = resources[d.Key.ToString()];
x.es = d.Value.ToString();
}
foreach (TransResource x in resources.Values)
{
string[] row = { x.id, x.en, x.fr, x.es };
var listViewItem = new ListViewItem(row);
listResx.Items.Add(listViewItem);
}
What I want to do is query all of the results in this ListView against what is entered in textboxQuery. If any field in the entire listview contains the string from textboxQuery I want it to be displayed in a new listview (lets say listviewQueryresult). I've had many failed attempts at this but I know it is possible through LINQ.
Because ListView.Items implements IEnumerable, but does not implement IEnumerable<T> you have to cast it to IEnumerable<ListViewItem> first, to query it using LINQ to Objects:
var results = listResx.Items.Cast<ListViewItem>()
.Where(x => YourPredicate(x));
If any field in the entire listview contains the string from
textboxQuery I want it to then be displayed in a new listview (lets
say listviewQueryresult)
For that, the predicate would be just:
var results = listResx.Items.Cast<ListViewItem>()
.Where(x => x.Text.Contains(textboxQuery));

Check if item is found in text file

How can I check if a text file contains an item from a listbox. To stop saving duplicates. I'm not sure what I'd add to this. This is called on a button click event. For example, if a duplicate was found, I could show MessageBox.Show ("duplicate error");
using (StreamWriter writer = new StreamWriter("test.txt", true))
{
foreach (object item in listBox2.Items)
{
writer.WriteLine(item.ToString());
}
}
Before writing to "test.txt", enumerate its contents:
var fileLines = File.ReadAllLines("test.txt");
List<string> fileItems = new List<string>(fileLines);
Then before you write each item, check to see if the list contains it:
using (StreamWriter writer = new StreamWriter("test.txt", true))
{
foreach (object item in listBox2.Items)
{
if (fileItems.Contains(item))
// Do something, break, etc.
else
writer.WriteLine(item.ToString());
}
}
Edit:
Per suggestions, you can use a HashSet instead of a List for performance, as it can only contain unique values.
Another improvement may be to check if any duplicates exist before writing anything to the file. I've done that in the example below in a LINQ statement:
var fileLines = File.ReadAllLines("test.txt");
HashSet<string> fileItems = new HashSet<string>(fileLines);
using (StreamWriter writer = new StreamWriter("test.txt", true))
{
bool duplicateFound = fileItems.Any(fi => listBox1.Items.Cast<string>().Any(i => i == fi));
if (duplicateFound)
MessageBox.Show("Duplicate items found.");
else
foreach (object item in listBox1.Items)
writer.WriteLine(item.ToString());
}
Edit 2:
As #Servy suggested, the listbox could contain duplicates, which should also be taken into consideration. Additionally, my HashSet implementation was sub-par. So in this third example, I am first checking if the listbox contains duplicates, then if any of the listbox items are already in the file. The usage of HashSet is more performant as well because I am not iterating it.
var fileLines = File.ReadAllLines("test.txt");
HashSet<string> fileItems = new HashSet<string>(fileLines);
List<string> duplicateListboxItems = listBox1.Items.Cast<string>().GroupBy(l => l).Where(g => g.Count() > 1).Select(g => g.Key).ToList();
if (duplicateListboxItems.Count > 0)
{
MessageBox.Show("The listbox contains duplicate entries.");
return;
}
bool duplicateFound = false;
List<string> outputItems = new List<string>();
foreach (string item in listBox1.Items)
{
if (fileItems.Contains(item))
{
MessageBox.Show(String.Format("The file has a duplicate: {0}", item));
duplicateFound = true;
break;
}
outputItems.Add(item);
}
if (duplicateFound)
return;
using (StreamWriter writer = new StreamWriter("test.txt", true))
{
foreach (string s in outputItems)
writer.WriteLine(s);
}
string filePath = "test.txt";
var existingLines = new HashSet<string>(File.ReadAllLines(filePath));
var linesToWrite = new List<string>();
foreach (string item in listBox2.Items)
{
if (existingLines.Add(item))
{
linesToWrite.Add(item);
}
else
{
//this is a duplicate!!!
}
}
File.AppendAllLines(filePath, linesToWrite);

Categories

Resources