I've been trying to write my own quicksort in C#.
However, when I concatenate the lower, pivot and greater partitions, Visual Studio tells me it cant convert void to string list, though I'm not sure how the concatenated lessList and moreList even becomes void.
Code snippet:
public List<string> SortFiles(List<string> fileList)
{
int listSize = fileList.Count;
if (listSize <= 1)
{
return fileList; //because it is already sorted
}
string pivotString = fileList[listSize/2];
//partition list
List<string> lessList = new List<string>();
List<string> moreList = new List<string>();
foreach (string g in fileList)
{
if (String.Compare(g, pivotString) <= 0)
{
lessList.Add(g);
}
else
{
moreList.Add(g);
}
}
lessList = SortFiles(lessList);
moreList = SortFiles(moreList);
//concatenate lessList + pivot + moreList
List<string> sortedFiles = lessList.Add(pivotString); //thinks is void
sortedFiles = sortedFiles.AddRange(moreList); //thinks is void
return sortedFiles;
The fact is that Add and AddRange don't return a new list with the added element (or range).
Rather, they add the element(s) to the list, modifying it in-place.
In fact you can simply modify the code to just do the Add and AddRange, and it will work.
Note though that it will be rather inefficient, as you are creating lists and copying thing around a lot.
Related
I was working on finding out the Common string part in the String list. If we take a sample data set
private readonly List<string> Xpath = new List<string>()
{
"BODY>MAIN:nth-of-type(1)>DIV>SECTION>DIV>SECTION>DIV>DIV:nth-of-type(1)>DIV>DIV:nth-of-type(3)>DIV>ARTICLE>DIV>DIV>DIV>SECTION:nth-of-type(1)>H2:nth-of-type(1)",
"BODY>MAIN:nth-of-type(1)>DIV>SECTION>DIV>SECTION>DIV>DIV:nth-of-type(1)>DIV>DIV:nth-of-type(3)>DIV>ARTICLE>DIV>DIV>DIV>SECTION:nth-of-type(2)>H2:nth-of-type(1)",
"BODY>MAIN:nth-of-type(1)>DIV>SECTION>DIV>SECTION>DIV>DIV:nth-of-type(1)>DIV>DIV:nth-of-type(3)>DIV>ARTICLE>DIV>DIV>DIV>SECTION:nth-of-type(3)>H2:nth-of-type(1)",
"BODY>MAIN:nth-of-type(1)>DIV>SECTION>DIV>SECTION>DIV>DIV:nth-of-type(1)>DIV>DIV:nth-of-type(3)>DIV>ARTICLE>DIV>DIV>DIV>SECTION:nth-of-type(4)>H2:nth-of-type(1)",
"BODY>MAIN:nth-of-type(1)>DIV>SECTION>DIV>SECTION>DIV>DIV:nth-of-type(1)>DIV>DIV:nth-of-type(3)>DIV>ARTICLE>DIV>DIV>DIV>SECTION:nth-of-type(5)>H2:nth-of-type(1)",
"BODY>MAIN:nth-of-type(1)>DIV>SECTION>DIV>SECTION>DIV>DIV:nth-of-type(1)>DIV>DIV:nth-of-type(3)>DIV>ARTICLE>DIV>DIV>DIV>SECTION:nth-of-type(6)>H2:nth-of-type(1)",
"BODY>MAIN:nth-of-type(1)>DIV>SECTION>DIV>SECTION>DIV>DIV:nth-of-type(1)>DIV>DIV:nth-of-type(3)>DIV>ARTICLE>DIV>DIV>DIV>SECTION:nth-of-type(7)>H2:nth-of-type(1)",
"BODY>MAIN:nth-of-type(1)>DIV>SECTION>DIV>SECTION>DIV>DIV:nth-of-type(1)>DIV>DIV:nth-of-type(3)>DIV>ARTICLE>DIV>DIV>DIV>SECTION:nth-of-type(8)>H2:nth-of-type(1)",
"BODY>MAIN:nth-of-type(1)>DIV>SECTION>DIV>SECTION>DIV>DIV:nth-of-type(1)>DIV>DIV:nth-of-type(3)>DIV>ARTICLE>DIV>DIV>DIV>SECTION:nth-of-type(9)>H2:nth-of-type(1)"
};
From this, I want to find out to which children these are similar. data is an Xpath list.
Programmatically I should be able to tell
Expected output:
BODY>MAIN:nth-of-type(1)>DIV>SECTION>DIV>SECTION>DIV>DIV:nth-of-type(1)>DIV>DIV:nth-of-type(3)>DIV>ARTICLE>DIV>DIV>DIV
In order to get this What I did was like this. I separate each item by > and then create a list of items for each dataset originally.
Then using this find out what are the unique items
private IEnumerable<T> GetCommonItems<T>(IEnumerable<T>[] lists)
{
HashSet<T> hs = new HashSet<T>(lists.First());
for (int i = 1; i < lists.Length; i++)
{
hs.IntersectWith(lists[i]);
}
return hs;
}
Able to find out the unique values and create a dataset again. But what happened is if this contains Ex:- Div in two places and it also in every originally dataset even then this method will pick up only one Div.
From then I would get something like this:
BODY>MAIN:nth-of-type(1)>DIV>SECTION
But I need this
BODY>MAIN:nth-of-type(1)>DIV>SECTION>DIV>SECTION>DIV>DIV:nth-of-type(1)>DIV>DIV:nth-of-
type(3)>DIV>ARTICLE>DIV>DIV>DIV
Disclaimer: This is not the most performant solution but it works :)
Let's start with splitting the first path by > character
Do the same with all the paths
char separator = '>';
IEnumerable<string> firstPathChunks = Xpath[0].Split(separator);
var chunks = Xpath.Select(path => path.Split(separator).ToList()).ToArray();
Iterate through the firstPathChunks
Iterate through the chunks
if there is a match then remove the first element
if all first element is removed then append the matching prefix to sb
void Process(StringBuilder sb)
{
foreach (var pathChunk in firstPathChunks)
{
foreach (var chunk in chunks)
{
if (chunk[0] != pathChunk)
{
return;
}
chunk.RemoveAt(0);
}
sb.Append(pathChunk);
sb.Append(separator);
}
}
Sample usage
var sb = new StringBuilder();
Process(sb);
Console.WriteLine(sb.ToString());
Output
BODY>MAIN:nth-of-type(1)>DIV>SECTION>DIV>SECTION>DIV>DIV:nth-of-type(1)>DIV>DIV:nth-of-type(3)>DIV>ARTICLE>DIV>DIV>DIV>
Parsing the string by the seperator > is a good idea. Instead of then creating a list of unique items you should create a list of all items contained in the string which would result in
{
"BODY",
"MAIN:nth-of-type(1)",
"DIV",
"SECTTION",
"DIV",
...
}
for the first entry of your XPath list.
This way you create a List<List<string>> containing every element of each entry of your XPath list. You then can compare all first elements of the inner lists. If they are equal save that elements value to you output and proceed with all second elements and so on until you find an element that is not equal in all outer lists.
Edit:
After seperating your list by the > seperator this could look something like this:
List<List<string>> XPathElementsLists;
List<string> resultElements = new List<string>();
string result;
XPathElementsLists = ParseElementsFormXPath(XPath);
for (int i = 0; i < XPathElementsLists[0].Count; i++)
{
bool isEqual = true;
string compareElemment = XPathElementsLists[0][i];
foreach (List<string> element in XPathElementsLists)
{
if (!String.Equals(compareElemment, element))
{
isEqual = false;
break;
}
}
if (!isEqual)
{
break;
}
resultElements.Add(compareElemment);
}
result = String.Join(">", resultElements.ToArray());
I have some code that's main purpose is to combine multiple like lists into one master list to return with the View.
ActivityAuditDetails searchParams = new ActivityAuditDetails();
ActivityAuditDetails finalResults = new ActivityAuditDetails();
List<string> finalChangedColumns = new List<string>();
List<string> finalOldValues = new List<string>();
List<string> finalNewValues = new List<string>();
string finalAuditAction = string.Empty;
List<int> auditKeys = AuditIdentityId.Split(',').Select(int.Parse).ToList();
string url = "/Audit/GetActivityAuditDetails";
try
{
foreach (int auditKey in auditKeys)
{
searchParams.AuditIdentityId = auditKey;
ActivityAuditDetails result = // SOME METHOD THAT RETURNS RESULTS AS IT SHOULD;
finalChangedColumns.Concat(result.ChangedColumns);
finalAuditAction = result.AuditAction;
finalOldValues.Concat(result.OldValues);
finalNewValues.Concat(result.NewValues);
}
finalResults.ChangedColumns = finalChangedColumns;
finalResults.AuditAction = finalAuditAction;
finalResults.OldValues = finalOldValues;
finalResults.NewValues = finalNewValues;
}
catch (Exception e)
{
e.ToLog();
}
return View(finalResults);
I can see that the result object is populated as it should be in the debugger. I thought the Concat method would work to combine the lists, but my final values in the foreach loop never get update\incremented ( the list count remains zero ).
Is there another way to accomplish this, or am I having a morning brain fart? My question was not about the differences, as I was aware of them. I just had a momentary lapse of reason.
You likely want to use AddRange rather than Concat.
The former adds the data directly to the List. The latter concatenates data into a new IEnumerable.
But because you are not assigning the result of Concat to anything (i.e. var g = finalChangedColumns.Concat(result.ChangedColumns);) your Concat calls do effectively nothing.
List<T>.AddRange(IEnumerable<T> collection) (link to info) probably does what you're looking for, right?
Adds the elements of the specified collection to the end of the List.
From documentation:
string[] input = { "Brachiosaurus",
"Amargasaurus",
"Mamenchisaurus" };
List<string> dinosaurs = new List<string>();
dinosaurs.AddRange(input);
//The list now contains all dinosaurs from the input string array
I am somewhat struggling with the terminology and complexity of my explanations here, feel free to edit it.
I have 1.000 - 20.000 objects. Each one can contain several name words (first, second, middle, last, title...) and normalized numbers(home, business...), email adresses or even physical adresses and spouse names.
I want to implement a search that enables users to freely combine word parts and number parts.When I search for "LL 676" I want to find all objects that contain any String with "LL" AND "676".
Currently I am iterating over every object and every objects property, split the searchString on " " and do a stringInstance.Contains(searchword).
This is too slow, so I am looking for a better solution.
What is the appropriate language agnostic data structure for this?
In my case I need it for C#.
Is the following data structure a good solution?
It's based on a HashMap/Dictionary.
At first I create a String that contains all name parts and phone numbers I want to look through, one example would be: "William Bill Henry Gates III 3. +436760000 billgatesstreet 12":
Then I split on " " and for every word x I create all possible substrings y that fullfill x.contains(y). I put every of those substrings inside the hashmap/dictionary.
On lookup/search I just need to call the search for every searchword and the join the results. Naturally, the lookup speed is blazingly fast (native Hashmap/Dictionary speed).
EDIT: Inserts are very fast as well (insignificant time) now that I use a smarter algorithm to get the substrings.
It's possible I've misunderstood your algorithm or requirement, but this seems like it could be a potential performance improvement:
foreach (string arg in searchWords)
{
if (String.IsNullOrEmpty(arg))
continue;
tempList = new List<T>();
if (dictionary.ContainsKey(arg))
foreach (T obj in dictionary[arg])
if (list.Contains(obj))
tempList.Add(obj);
list = new List<T>(tempList);
}
The idea is that you do the first search word separately before this, and only put all the subsequent words into the searchWords list.
That should allow you to remove your final foreach loop entirely. Results only stay in your list as long as they keep matching every searchWord, rather than initially having to pile everything that matches a single word in then filter them back out at the end.
In case anyone cares for my solution:
Disclaimer:
This is only a rough draft.
I have only done some synthetic testing and I have written a lot of it without testing it again.I have revised my code: Inserts are now ((n^2)/2)+(n/2) instead of 2^n-1 which is infinitely faster. Word length is now irrelevant.
namespace MegaHash
{
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading.Tasks;
public class GenericConcurrentMegaHash<T>
{
// After doing a bulk add, call AwaitAll() to ensure all data was added!
private ConcurrentBag<Task> bag = new ConcurrentBag<Task>();
private ConcurrentDictionary<string, List<T>> dictionary = new ConcurrentDictionary<string, List<T>>();
// consider changing this to include for example '-'
public char[] splitChars;
public GenericConcurrentMegaHash()
: this(new char[] { ' ' })
{
}
public GenericConcurrentMegaHash(char[] splitChars)
{
this.splitChars = splitChars;
}
public void Add(string keyWords, T o)
{
keyWords = keyWords.ToUpper();
foreach (string keyWord in keyWords.Split(splitChars))
{
if (keyWord == null || keyWord.Length < 1)
return;
this.bag.Add(Task.Factory.StartNew(() => { AddInternal(keyWord, o); }));
}
}
public void AwaitAll()
{
lock (this.bag)
{
foreach (Task t in bag)
t.Wait();
this.bag = new ConcurrentBag<Task>();
}
}
private void AddInternal(string key, T y)
{
for (int i = 0; i < key.Length; i++)
{
for (int i2 = 0; i2 < i + 1; i2++)
{
string desire = key.Substring(i2, key.Length - i);
if (dictionary.ContainsKey(desire))
{
List<T> l = dictionary[desire];
lock (l)
{
try
{
if (!l.Contains(y))
l.Add(y);
}
catch (Exception ex)
{
ex.ToString();
}
}
}
else
{
List<T> l = new List<T>();
l.Add(y);
dictionary[desire] = l;
}
}
}
}
public IList<T> FulltextSearch(string searchString)
{
searchString = searchString.ToUpper();
List<T> list = new List<T>();
string[] searchWords = searchString.Split(splitChars);
foreach (string arg in searchWords)
{
if (arg == null || arg.Length < 1)
continue;
if (dictionary.ContainsKey(arg))
foreach (T obj in dictionary[arg])
if (!list.Contains(obj))
list.Add(obj);
}
List<T> returnList = new List<T>();
foreach (T o in list)
{
foreach (string arg in searchWords)
if (dictionary[arg] == null || !dictionary[arg].Contains(o))
goto BREAK;
returnList.Add(o);
BREAK:
continue;
}
return returnList;
}
}
}
In my C# window application I need wild card search on list box.
i.e If I write some text in textbox it should be auto selected in that list box.
List box is binding using datatable e.g lstVendor.datasource = l_dtTable
Findstring() function is finding match only for starting string. But I need if match find at any position in particular text then it should be highlighted.
I am Using below code but not getting index/or even lstVendor.selecteditem = "string" not working.
Indexof() always return -1
string final = "";
foreach (Object lstItem in lstVendor.Items)
{
string s = ((DataRowView)(lstItem)).Row.ItemArray[0].ToString();
if (s.ToLower().Contains(txtVendor.Text.ToLower()))
{
int i = lstVendor.Items.IndexOf(s);
final += s + ",";
}
}
string[] l_strArrVendorList = final.TrimEnd(',').Split(',');
for (int Counter = 0; Counter < l_strArrVendorList.Length; Counter++)
{
lstVendor.SelectedItem = l_strArrVendorList[Counter];
}
Searching may return multiple matched items, this code will find the first matched items:
var firstMatched = listBox1.Items.Cast<DataRowView>()
.Where(v=>Convert.ToString(v.Row[0]).ToLower()
.Contains(txtVendor.Text.ToLower()))
.FirstOrDefault();
if(firstMatched != null) listBox1.SelectedItem = firstMatched;
You can remove the FirstOrDefault() to get the list of matched items and implement some navigation through the matched items.
For VS 2000 support, you have to use this extension class:
public static class EnumerableExtension {
public static IEnumerable<T> Cast<T>(this System.Collections.IEnumerable source){
foreach(var item in source)
yield return (T)item;
}
}
My code above should work OK for you, but looks like you just want your code to be fixed instead, here is the fixed code for you:
//Note that you need to set SelectionMode for your listBox like this:
lstVendor.SelectionMode = SelectionMode.MultiSimple;
foreach (var lstItem in lstVendor.Items.Cast<DataRowView>()) {
string s = lstItem.Row.ItemArray[0].ToString();
if (s.ToLower().Contains(txtVendor.Text.ToLower())) {
lstVendor.SelectedItems.Add(lstItem);
}
}
How can I dissect or retrieve string values?
Here's the sample code that I'm working on now:
private void SplitStrings()
{
List<string> listvalues = new List<string>();
listvalues = (List<string>)Session["mylist"];
string[] strvalues = listvalues.ToArray();
for (int x = 0; x < strvalues.Length; x++)
{
}
}
Now that I'am able to retrieve list values in my session. How can I separately get the values of each list using foreach or for statement?
What I want to happen is to programmatically split the values of the strings depending on how many is in the list.
If you have a list of string values, you can do the following:
private void SplitStrings()
{
List<string> listValues = (List<string>) Session["mylist"];
// always check session values for null
if(listValues != null)
{
// go through each list item
foreach(string stringElement in listValues)
{
// do something with variable 'stringElement'
System.Console.WriteLine(stringElement);
}
}
}
Note that I test the result of casting the session and that I don't create a new list first-off, which is not necessary. Also note that I don't convert to an array, simply because looping a list is actually easier, or just as easy, as looping an array.
Note that you named your method SplitStrings, but we're not splitting anything. Did you mean to split something like "one;two;three;four" in a four-element list, based on the separator character?
I'm not sure what you're trying to obtain in this code, I don't know why you're converting your List to an Array.
You can loop through your listValues collection with a foreach block:
foreach(string value in listValues)
{
//do something with value, I.e.
Response.Write(value);
}
I don't know what's in the strings but you can start by simplifying. There is no point allocating a new List if you're going to overwrite it immediately.
private void SplitStrings()
{
List<string> list = (List<string>)Session["mylist"];
foreach(string value in list)
{
}
}
List listvalues = (List)Session["mylist"];
foreach (string s in listvalues)
{
//do what you want with s here
}