I have an object;
class MyObject
{
double Timestamp;
string Originalname;
string Renamedname;
}
I have an array of MyObject
1538148190,"C:\test\jeff.txt","jeffmodified.txt"
1538148200,"C:\test\jeffmodified.txt","jeffmodified2.txt"
1538148289,"C:\test\jeffmodified2.txt","jeffmodified3.txt"
1538149002,"C:\test\thing.txt","something.txt"
I am trying to create a correlation of the data to present the idea that jeff.txt was renamed to jeffmodified.tx and then to jeff.modified2.txt and jeffmodified3.txt. The timestamp (unix format) shows me that jeff.txt was the original file name.
Can this be done in linq? This is working with objects in memory only - not a database and the max number of objects would generally be 200 objects.
Using linq this cannot be done in a one or two liner (if that is what you meant by can this be done in linq. But due to linq the solution does become easier then what it would have been without linq.
The important assumptions that I have made in the solution below is that the timestamp will increase with each file name change. Solution is explained in the comments provided.
static void Main(string[] args)
{
// setting up test data, can be removed from actual solution where objects can be populated as required
List<MyObject> objects = new List<MyObject>();
objects = GetData();
int i = 0;
int key = 0;
List<WrapMyObject> wObjs = new List<WrapMyObject>();
Dictionary<int, string> origDictionary = new Dictionary<int, string>();
Dictionary<int, string> searchDictionary = new Dictionary<int, string>();
// adding the directory path to the renamed filename as well, since same filename can be there for different filepath
Func<string, string> GetFilePath = (path) => path.Substring(0, path.LastIndexOf('\\'));
objects.ForEach(x=>x.Renamedname = GetFilePath(x.Originalname) + "\\" + x.Renamedname);
//sort the whole list by timestamp, this should ensure that we begin always with the orignal file and always get subsequent changed filename in the list
//the algorithm relies on the fact that the renamed name should be the next orignal name for that file change in the sorted list
//if current item's orignal name is not in search dictory then we are seeing this file path first time, add to orignal dictionary with key, add the renamed name in search dictionary with same key
//else add add the search path found in searchDictionary back into origDictionary and update the searchDict with the new path value (current item renamed path)
var sortedObjects = objects.OrderBy(o => o.Timestamp);
foreach (var item in objects)
{
if (searchDictionary.ContainsValue(item.Originalname))
{
key = searchDictionary.Where(x => x.Value == item.Originalname).First().Key;
origDictionary[key] = origDictionary[key] + "!" + searchDictionary[key];
searchDictionary[key] = item.Renamedname;
}
else
{
origDictionary.Add(i, item.Originalname);
searchDictionary.Add(i, item.Renamedname);
}
i++;
}
//iterated the whole list of objects....which means the final node would be remaining in the search dictionary, add them back to origDictionary to complete the modified path
searchDictionary.ToList().ForEach(x => origDictionary[x.Key] = origDictionary[x.Key] + "!" + searchDictionary[x.Key]);
//at this point orgnalDict has the list of all names that each file was modified with.
}
Related
Somebody told me that the key of a dictionary should be unique, so i changed my dictionary from:
Dictionary<string, int> recipients = new Dictionary<string, int>();
to
Dictionary<int, string> recipients = new Dictionary<int, string>();
Because the int is unique in my case, and the string isn't per se.
But first i could get the int from a picker aswell, but now i cant figure it out.
The picker:
pickerRecipients = new Picker
{
};
foreach (string recipientName in recipients.Values)
{
pickerRecipients.Items.Add(recipientName);
}
First i would do
string currentlySelected = pickerRecipients.Items[pickerRecipients.SelectedIndex];
But now it obviously doesnt work, but i can't find a way to get the int (the picker shows the string, which is a name, the int thats connected to it is an unique user number)
// get the key of the first match
var key = recipients.FirstOrDefault(x => x.Value == selectedName).Key;
I don't have enough rep to put it as a comment but your code will have problems. For example if u have this values in your dictionary : '1 someValue', '2 otherValue', '3 someValue' and the user will pick '3 someValue'. Your code will return 1.
I think you could try to insert (I entered a SPACE before the ( so i can split by ' ')
foreach (var recipient in recipients)
{
pickerRecipients.Items.Add(recipient.Value + " (" + recipient.Key + ")");
}
then get the user selected value with
string currentlySelected = pickerRecipients.Items[pickerRecipients.SelectedIndex];
split the result like
var selectedIndex = currentlySelected.Split(' ').Last().Split('(', ')')[1];
and you get your selected index.
Again, this is not a direct solution to your problem but your problem will generate other problems if you don't take this into consideration.
Given an array of strings stored within a dictionary, I'd like to access the first element stored in the array. How would I go about doing that?
Here's what I have already written...
public IC2Engineering GetReportResultsTableByXPath(string xPath, int rowNumber) {
WaitForComplete();
IWebElement table = FindElement(By.XPath(xPath));
//get an array of rows from the table
IList<IWebElement> table_rows = table.FindElements(By.TagName("tr"));
//store the row data
IList<IWebElement> row_data = table_rows[rowNumber].FindElements(By.XPath("td"));
int col_num;
col_num = 0;
foreach (IWebElement cell in row_data) {
Console.WriteLine("col #" + col_num + ": " + row_data[col_num].Text.ToString());
col_num++;
}
return this;
}
So I know that the strings are being stored properly, because the data written to the console output is correct.
How can I store the data from each one of those outputs into it's own separate "spot" in a SharedProperty Dictionary called "TableRowData"?
And after storing those outputs into their own location within the dictionary, how can I access that first entry in the dictionary?
I have a strong feeling that the code I am looking for must execute within the foreach loop, but I am can not figure out how to do that...
If I understand you right, the problem is here:
SeleniumUITests.SharedProperties["ReportRowData"] = row_data.ToString();
You are storing the "array" (really a List<T>) as a string in the dictionary, not the array itself. It seems like you want:
SeleniumUITests.SharedProperties["ReportRowData"] = row_data;
Console.WriteLine(SeleniumUITests.SharedProperties["ReportRowData"][0]);
I'm new to C# and programming as a whole and I've been unable to come up with a solution to what I want to do. I want to be able to create a way to display several arrays containing elements from three external text files with values on each line (e.g. #"Files\Column1.txt", #"Files\Column2.txt" #"Files\Column3.txt"). They then need to be displayed like this in the command line:
https://www.dropbox.com/s/0telh1ils201wpy/Untitled.png?dl=0
I also need to be able to sort each column individually (e.g. column 3 from lowest to highest).
I've probably explained this horribly but I'm not sure how else to put it! Any possible solutions will be greatly appreciated!
One way to do it would be to store the corresponding items from each file in a Tuple, and then store those in a List. This way the items will all stay together, but you can sort your list on any of the Tuple fields. If you were doing anything more detailed with these items, I would suggest creating a simple class to store them, so the code would be more maintainable.
Something like:
public class Item
{
public DayOfWeek Day { get; set; }
public DateTime Date { get; set; }
public string Value { get; set; }
}
The example below could easily be converted to use such a class, but for now it uses a Tuple<string, string, string>. As an intermediate step, you could easily convert the items as you create the Tuple to get more strongly-typed versions, for example, you could have Tuple<DayOfWeek, DateTime, string>.
Here's the sample code for reading your file items into a list, and how to sort on each item type:
public static void Main()
{
// For testing sake, I created some dummy files
var file1 = #"D:\Public\Temp\File1.txt";
var file2 = #"D:\Public\Temp\File2.txt";
var file3 = #"D:\Public\Temp\File3.txt";
// Validation that files exist and have same number
// of items is intentionally left out for the example
// Read the contents of each file into a separate variable
var days = File.ReadAllLines(file1);
var dates = File.ReadAllLines(file2);
var values = File.ReadAllLines(file3);
var itemCount = days.Length;
// The list of items read from each file
var fileItems = new List<Tuple<string, string, string>>();
// Add a new item for each line in each file
for (int i = 0; i < itemCount; i++)
{
fileItems.Add(new Tuple<string, string, string>(
days[i], dates[i], values[i]));
}
// Display the items in console window
fileItems.ForEach(item =>
Console.WriteLine("{0} {1} = {2}",
item.Item1, item.Item2, item.Item3));
// Example for how to order the items:
// By days
fileItems = fileItems.OrderBy(item => item.Item1).ToList();
// By dates
fileItems = fileItems.OrderBy(item => item.Item2).ToList();
// By values
fileItems = fileItems.OrderBy(item => item.Item3).ToList();
// Order by descending
fileItems = fileItems.OrderByDescending(item => item.Item1).ToList();
// Show the values based on the last ordering
fileItems.ForEach(item =>
Console.WriteLine("{0} {1} = {2}",
item.Item1, item.Item2, item.Item3));
}
I have a flat file with an unfortunately dynamic column structure. There is a value that is in a hierarchy of values, and each tier in the hierarchy gets its own column. For example, my flat file might resemble this:
StatisticID|FileId|Tier0ObjectId|Tier1ObjectId|Tier2ObjectId|Tier3ObjectId|Status
1234|7890|abcd|efgh|ijkl|mnop|Pending
...
The same feed the next day may resemble this:
StatisticID|FileId|Tier0ObjectId|Tier1ObjectId|Tier2ObjectId|Status
1234|7890|abcd|efgh|ijkl|Complete
...
The thing is, I don't care much about all the tiers; I only care about the id of the last (bottom) tier, and all the other row data that is not a part of the tier columns. I need normalize the feed to something resembling this to inject into a relational database:
StatisticID|FileId|ObjectId|Status
1234|7890|ijkl|Complete
...
What would be an efficient, easy-to-read mechanism for determining the last tier object id, and organizing the data as described? Every attempt I've made feels kludgy to me.
Some things I've done:
I have tried to examine the column names for regular expression patterns, identify the columns that are tiered, order them by name descending, and select the first record... but I lose the ordinal column number this way, so that didn't look good.
I have placed the columns I want into an IDictionary<string, int> object to reference, but again reliably collecting the ordinal of the dynamic columns is an issue, and it seems this would be rather non-performant.
I ran into a simular problem a few years ago. I used a Dictionary to map the columns, it was not pretty, but it worked.
First make a Dictionary:
private Dictionary<int, int> GetColumnDictionary(string headerLine)
{
Dictionary<int, int> columnDictionary = new Dictionary<int, int>();
List<string> columnNames = headerLine.Split('|').ToList();
string maxTierObjectColumnName = GetMaxTierObjectColumnName(columnNames);
for (int index = 0; index < columnNames.Count; index++)
{
if (columnNames[index] == "StatisticID")
{
columnDictionary.Add(0, index);
}
if (columnNames[index] == "FileId")
{
columnDictionary.Add(1, index);
}
if (columnNames[index] == maxTierObjectColumnName)
{
columnDictionary.Add(2, index);
}
if (columnNames[index] == "Status")
{
columnDictionary.Add(3, index);
}
}
return columnDictionary;
}
private string GetMaxTierObjectColumnName(List<string> columnNames)
{
// Edit this function if Tier ObjectId is greater then 9
var maxTierObjectColumnName = columnNames.Where(c => c.Contains("Tier") && c.Contains("Object")).OrderBy(c => c).Last();
return maxTierObjectColumnName;
}
And after that it's simply running thru the file:
private List<DataObject> ParseFile(string fileName)
{
StreamReader streamReader = new StreamReader(fileName);
string headerLine = streamReader.ReadLine();
Dictionary<int, int> columnDictionary = this.GetColumnDictionary(headerLine);
string line;
List<DataObject> dataObjects = new List<DataObject>();
while ((line = streamReader.ReadLine()) != null)
{
var lineValues = line.Split('|');
string statId = lineValues[columnDictionary[0]];
dataObjects.Add(
new DataObject()
{
StatisticId = lineValues[columnDictionary[0]],
FileId = lineValues[columnDictionary[1]],
ObjectId = lineValues[columnDictionary[2]],
Status = lineValues[columnDictionary[3]]
}
);
}
return dataObjects;
}
I hope this helps (even a little bit).
Personally I would not try to reformat your file. I think the easiest approach would be to parse each row from the front and the back. For example:
itemArray = getMyItems();
statisticId = itemArray[0];
fileId = itemArray[1];
//and so on for the rest of your pre-tier columns
//Then get the second to last column which will be the last tier
lastTierId = itemArray[itemArray.length -1];
Since you know the last tier will always be second from the end you can just start at the end and work your way forwards. This seems like it would be much easier than trying to reformat the datafile.
If you really want to create a new file, you could use this approach to get the data you want to write out.
I don't know C# syntax, but something along these lines:
split line in parts with | as separator
get parts [0], [1], [length - 2] and [length - 1]
pass the parts to the database handling code
I have a basic C# console application that reads a text file (CSV format) line by line and puts the data into a HashTable. The first CSV item in the line is the key (id num) and the rest of the line is the value. However I've discovered that my import file has a few duplicate keys that it shouldn't have. When I try to import the file the application errors out because you can't have duplicate keys in a HashTable. I want my program to be able to handle this error though. When I run into a duplicate key I would like to put that key into a arraylist and continue importing the rest of the data into the hashtable. How can I do this in C#
Here is my code:
private static Hashtable importFile(Hashtable myHashtable, String myFileName)
{
StreamReader sr = new StreamReader(myFileName);
CSVReader csvReader = new CSVReader();
ArrayList tempArray = new ArrayList();
int count = 0;
while (!sr.EndOfStream)
{
String temp = sr.ReadLine();
if (temp.StartsWith(" "))
{
ServMissing.Add(temp);
}
else
{
tempArray = csvReader.CSVParser(temp);
Boolean first = true;
String key = "";
String value = "";
foreach (String x in tempArray)
{
if (first)
{
key = x;
first = false;
}
else
{
value += x + ",";
}
}
myHashtable.Add(key, value);
}
count++;
}
Console.WriteLine("Import Count: " + count);
return myHashtable;
}
if (myHashtable.ContainsKey(key))
duplicates.Add(key);
else
myHashtable.Add(key, value);
A better solution is to call ContainsKey to check if the key exist before adding it to the hash table instead. Throwing exception on this kind of error is a performance hit and doesn't improve the program flow.
ContainsKey has a constant O(1) overhead for every item, while catching an Exception incurs a performance hit on JUST the duplicate items.
In most situations, I'd say check for the key, but in this case, its better to catch the exception.
Here is a solution which avoids multiple hits in the secondary list with a small overhead to all insertions:
Dictionary<T, List<K>> dict = new Dictionary<T, List<K>>();
//Insert item
if (!dict.ContainsKey(key))
dict[key] = new List<string>();
dict[key].Add(value);
You can wrap the dictionary in a type that hides this or put it in a method or even extension method on dictionary.
If you have more than 4 (for example) CSV values, it might be worth setting the value variable to use a StringBuilder as well since the string concatenation is a slow function.
Hmm, 1.7 Million lines? I hesitate to offer this for that kind of load.
Here's one way to do this using LINQ.
CSVReader csvReader = new CSVReader();
List<string> source = new List<string>();
using(StreamReader sr = new StreamReader(myFileName))
{
while (!sr.EndOfStream)
{
source.Add(sr.ReadLine());
}
}
List<string> ServMissing =
source
.Where(s => s.StartsWith(" ")
.ToList();
//--------------------------------------------------
List<IGrouping<string, string>> groupedSource =
(
from s in source
where !s.StartsWith(" ")
let parsed = csvReader.CSVParser(s)
where parsed.Any()
let first = parsed.First()
let rest = String.Join( "," , parsed.Skip(1).ToArray())
select new {first, rest}
)
.GroupBy(x => x.first, x => x.rest) //GroupBy(keySelector, elementSelector)
.ToList()
//--------------------------------------------------
List<string> myExtras = new List<string>();
foreach(IGrouping<string, string> g in groupedSource)
{
myHashTable.Add(g.Key, g.First());
if (g.Skip(1).Any())
{
myExtras.Add(g.Key);
}
}
Thank you all.
I ended up using the ContainsKey() method. It takes maybe 30 secs longer, which is fine for my purposes. I'm loading about 1.7 million lines and the program takes about 7 mins total to load up two files, compare them, and write out a few files. It only takes about 2 secs to do the compare and write out the files.