Parallel Webservice loop skipping - c#

I'm using a parallel loop to call a webservice because the individual for loop is too slow. However the results comes out skipping some of the item.
Code:
private void readCSV(string FilePath, string Extension)
{
switch (Extension)
{
case ".csv":
var reader = new StreamReader(File.OpenRead(FilePath));
int counter = 0;
List<int> phoneNo = new List<int>();
while (!reader.EndOfStream)
{
var line = reader.ReadLine();
phoneNo.Add(int.Parse(line));
}
reader.Close();
Parallel.For(0, phoneNo.Count, (index) =>
{
counter++;
Literal1.Text += counter + " " + phoneNo[index] + " " + webserviceClass123.callWebserviceMethod(phoneNo[index]) + "<br/>";
});
break;
}
}
So the results should be like (example)
1 4189291 40.10
2 5124910 23.10
3 5123145 12.11
...
...
50 4124919 20.58
but it comes out as
3 8581892 41.10
1 9281989 10.99
50 4199289 02.22
It is jumbled up, and it misses a lot of data
How do I get it to be in order and ensure that all the data is represented?

It's not at all clear that you should expect Literal1.Text += ... to be thread-safe. I would suggest you use the Parallel.For loop just to collect the data, and then change Literal1.Text afterwards.
For example, you could write:
var results = new WhateverType[phoneNo.Count];
Parallel.For(0, phoneNo.Count,
index => results[index] = webserviceClass123.callWebserviceMethod(phoneNo[index]));
var builder = new StringBuilder();
for (int i = 0; i < phoneNo.Count; i++)
{
builder.AppendFormat("{0} {1} {2}<br/>",
i, phoneNo[i], results[i]);
}
Literal1.Text = builder.ToString();
It would quite possibly be even cleaner to use Parallel LINQ:
var results = phoneNo
.AsParallel()
.Select(number => new {
number,
result = webserviceClass123.callWebserviceMethod(number)
})
.AsOrdered()
.ToList()
var builder = new StringBuilder();
foreach (int i = 0; i < results.Count; i++)
{
builder.AppendFormat("{0} {1} {2}<br/>",
i, result[i].number, results[i].result);
}
Literal1.Text = builder.ToString();

Related

How to find max value from struct?

public struct nariai
{
public string vardas;
public string pavarde;
public double eurai;
public double centai;
public double suma;
};
static void Main(string[] args)
{
double islaidos;
double surinktiPinigai = 0;
StreamReader failas = new StreamReader("nariai.txt");
string a = failas.ReadLine();
int nariuKiekis = int.Parse(a);
nariai[] narys = new nariai[nariuKiekis];
string[] info = new string[nariuKiekis];
for (int i = 0; i < nariuKiekis; i++)
{
info[i] = failas.ReadLine();
string[] informacija = info[i].Split(' ');
narys[i].vardas = informacija[0];
narys[i].pavarde = informacija[1];
narys[i].eurai = double.Parse(informacija[2]);
narys[i].centai = double.Parse(informacija[3]);
Console.WriteLine("{0} {1} {2} {3}", narys[i].vardas, narys[i].pavarde, narys[i].eurai, narys[i].centai);
}
for (int i = 0; i < nariuKiekis; i++)
{
islaidos = narys[i].eurai * 100 + narys[i].centai;
narys[i].suma = islaidos / 100 * 0.25;
islaidos = narys[i].suma;
Console.WriteLine(narys[i].suma);
surinktiPinigai = surinktiPinigai + islaidos;
}
Console.WriteLine("Surinkti pinigai bendroms išlaidoms: {0} Eurai.", surinktiPinigai);
}
File looks like that:
Command looks like that:
I need to find who has the biggest value in the structure "public double suma" and i need to write first name and last name of it.
What you want is to retrieve from the list the item with the max suma and then you can get it's other properties.
var item = narys.OrderByDescending(i => i.suma).FirstOrDefault();
var name = $"{item?.vardas} {item?.pavarde}";
Notice the use of the ?. operator available since C# 6.0 that verifies item is not null. If by the time you perform this querying you know for sure that collection is not empty then simply:
var item = narys.OrderByDescending(i => i.suma).First();
var name = $"{item.vardas} {item.pavarde}";
You can also use MoreLinq's MaxBy which:
Returns the maximal element of the given sequence, based on the given projection.
You can try using LINQ:
var maxNarys = narys.OrderByDescending(n => n.suma)
.FirstOrDefault();
And then access it's properties like this:
var maxNarvardas = maxNarys.vardas;
EDIT
Also looking at your code more closely:
it seems that you can concat your two loops and add a variables to find max:
var maxSuma = double.MinValue;
var maxIndex = -1;
for (int i = 0; i < nariuKiekis; i++)
{
info[i] = failas.ReadLine();
string[] informacija = info[i].Split(' ');
narys[i].vardas = informacija[0];
narys[i].pavarde = informacija[1];
narys[i].eurai = double.Parse(informacija[2]);
narys[i].centai = double.Parse(informacija[3]);
Console.WriteLine("{0} {1} {2} {3}", narys[i].vardas, narys[i].pavarde, narys[i].eurai, narys[i].centai);
islaidos = narys[i].eurai * 100 + narys[i].centai;
narys[i].suma = islaidos / 100 * 0.25;
islaidos = narys[i].suma;
if(maxSuma < narys[i].suma)
{
maxSuma = narys[i].suma;
maxIndex = i;
}
surinktiPinigai = surinktiPinigai + islaidos;
}
if(maxIndex>=0) Console.WriteLine(narys[maxIndex].suma);

C# Merging Two or more Text Files side by side

using (StreamWriter writer = File.CreateText(FinishedFile))
{
int lineNum = 0;
while (lineNum < FilesLineCount.Min())
{
for (int i = 0; i <= FilesToMerge.Count() - 1; i++)
{
if (i != FilesToMerge.Count() - 1)
{
var CurrentFile = File.ReadLines(FilesToMerge[i]).Skip(lineNum).Take(1);
string CurrentLine = string.Join("", CurrentFile);
writer.Write(CurrentLine + ",");
}
else
{
var CurrentFile = File.ReadLines(FilesToMerge[i]).Skip(lineNum).Take(1);
string CurrentLine = string.Join("", CurrentFile);
writer.Write(CurrentLine + "\n");
}
}
lineNum++;
}
}
The current way i am doing this is just too slow. I am merging files that are each 50k+ lines long with various amounts of data.
for ex:
File 1
1
2
3
4
File 2
4
3
2
1
i need this to merge into being a third fileFile 3
1,4
2,3
3,2
4,1P.S. The user can pick as many files as they want from any locations.
Thanks for the help.
You approach is slow because of the Skip and Take in the loops.
You could use a dictionary to collect all line-index' lines:
string[] allFileLocationsToMerge = { "filepath1", "filepath2", "..." };
var mergedLists = new Dictionary<int, List<string>>();
foreach (string file in allFileLocationsToMerge)
{
string[] allLines = File.ReadAllLines(file);
for (int lineIndex = 0; lineIndex < allLines.Length; lineIndex++)
{
bool indexKnown = mergedLists.TryGetValue(lineIndex, out List<string> allLinesAtIndex);
if (!indexKnown)
allLinesAtIndex = new List<string>();
allLinesAtIndex.Add(allLines[lineIndex]);
mergedLists[lineIndex] = allLinesAtIndex;
}
}
IEnumerable<string> mergeLines = mergedLists.Values.Select(list => string.Join(",", list));
File.WriteAllLines("targetPath", mergeLines);
Here's another approach - this implementation only stores in memory one set of lines from each file simultaneously, thus reducing memory pressure significantly (if that is an issue).
public static void MergeFiles(string output, params string[] inputs)
{
var files = inputs.Select(File.ReadLines).Select(iter => iter.GetEnumerator()).ToArray();
StringBuilder line = new StringBuilder();
bool any;
using (var outFile = File.CreateText(output))
{
do
{
line.Clear();
any = false;
foreach (var iter in files)
{
if (!iter.MoveNext())
continue;
if (line.Length != 0)
line.Append(", ");
line.Append(iter.Current);
any = true;
}
if (any)
outFile.WriteLine(line.ToString());
}
while (any);
}
foreach (var iter in files)
{
iter.Dispose();
}
}
This also handles files of different lengths.

Generate multiple unique strings in C#

For my project, I have to generate a list of unique strings.
Everything works fine, but my problem is that it is very slow at the end.
I've tried using Parallel-Loops, but I found out that my ConcurrentBag<T>, which I was using, is also slow.
Now I'm using a simple for-loop and List<T> and it is now a little bit faster, but also really slow.
Here's my code:
private List<string> Generate(int start, int end, bool allowDupes)
{
var list = new List<string>();
var generator = new StringGenerator(LowerCase, UpperCase, Digits, NumberOfCharacters);
for (var i = start; i < end; i++)
{
StringBuilder sb;
while (true)
{
sb = new StringBuilder();
for (var j = 0; j < NumberOfSegments; j++)
{
sb.Append(generator.GenerateRandomString());
if (j < NumberOfSegments - 1)
{
sb.Append(Delimiter);
}
}
if (!allowDupes)
{
if (list.Contains(sb.ToString()))
{
continue;
}
}
break;
}
list.Add(sb.ToString());
GeneratedStringCount = i + 1;
}
return new List<string>(list);
}
I've also talked to my teacher and he would use the same algorithm for generating these strings.
Do you know a better solution? (The GenerateRandomString() Method in StringGenerator is simple and does not consume much performance. list.Contains(xy) is consuming alot of resources. [Performance Analysis in Visual Studio])
List.Contains is slow. Use a HashSet instead.
private List<string> Generate(int start, int end, bool allowDupes)
{
var strings = new HashSet<string>();
var list = new List<string>();
var generator = new StringGenerator(LowerCase, UpperCase, Digits, NumberOfCharacters);
for (var i = start; i < end; i++)
{
while (true)
{
string randomString = GetRandomString();
if (allowDupes || strings.Add(randomString))
{
list.Add(randomString);
break;
}
}
GeneratedStringCount = i + 1;
}
return new List<string>(list);
}
private string GetRandomString()
{
var segments = Enumerable.Range(1, NumberOfSegments)
.Select(_ => generator.GenerateRandomString());
var result = string.Join(Delimeter, segments);
return result;
}
This still has the chance for slow performance, but you could remedy that with a smart GenerateRandomString function.
public static String GenerateEightCode( int codeLenght, Boolean isCaseSensitive)
{
char[] chars = GetCharsForCode(isCaseSensitive);
byte[] data = new byte[1];
RNGCryptoServiceProvider crypto = new RNGCryptoServiceProvider();
crypto.GetNonZeroBytes(data);
data = new byte[codeLenght];
crypto.GetNonZeroBytes(data);
StringBuilder sb = new StringBuilder(codeLenght);
foreach (byte b in data)
{
sb.Append(chars[b % (chars.Length)]);
}
string key = sb.ToString();
if (codeLenght == 8)
key = key.Substring(0, 4) + "-" + key.Substring(4, 4);
else if (codeLenght == 16)
key = key.Substring(0, 4) + "-" + key.Substring(4, 4) + "-" + key.Substring(8, 4) + "-" + key.Substring(12, 4);
return key.ToString();
}
private static char[] GetCharsForCode(Boolean isCaseSensitive)
{
// all - abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890
char[] chars = new char[58];
if (isCaseSensitive)
{
chars = "abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ123456789".ToCharArray();//počet unikátních kombinací 4 - 424 270, 8 - 1 916 797 311, 16 - 7.99601828013E+13
}
else
{
chars = new char[35];
chars = "ABCDEFGHIJKLMNPQRSTUVWXYZ123456789".ToCharArray();//počet unikátních kombinací 4 - 52 360, 8 - 23 535 820, 16 - 4 059 928 950
}
return chars;
}

Better way for the special concatenation of two strings

I want to concatenate two strings in such a way, that after the first character of the first string, the first character of second string comes, and then the second character of first string comes and then the second character of the second string comes and so on. Best explained by some example cases:
s1="Mark";
s2="Zukerberg"; //Output=> MZaurkkerberg
if:
s1="Zukerberg";
s2="Mark" //Output=> ZMuakrekrberg
if:
s1="Zukerberg";
s2="Zukerberg"; //Output=> ZZuukkeerrbbeerrgg
I've written the following code which gives the expected output but its seems to be a lot of code. Is there any more efficient way for doing this?
public void SpecialConcat(string s1, string s2)
{
string[] concatArray = new string[s1.Length + s2.Length];
int k = 0;
string final = string.Empty;
string superFinal = string.Empty;
for (int i = 0; i < s1.Length; i++)
{
for (int j = 0; j < s2.Length; j++)
{
if (i == j)
{
concatArray[k] = s1[i].ToString() + s2[j].ToString();
final = string.Join("", concatArray);
}
}
k++;
}
if (s1.Length > s2.Length)
{
string subOne = s1.Remove(0, s2.Length);
superFinal = final + subOne;
}
else if (s2.Length > s1.Length)
{
string subTwo = s2.Remove(0, s1.Length);
superFinal = final + subTwo;
}
else
{
superFinal = final;
}
Response.Write(superFinal);
}
}
I have written the same logic in Javascript also, which works fine but again a lot of code.
var s1 = "Mark";
var s2 = "Zukerberg";
var common = string.Concat(s1.Zip(s2, (a, b) => new[]{a, b}).SelectMany(c => c));
var shortestLength = Math.Min(s1.Length, s2.Length);
var result =
common + s1.Substring(shortestLength) + s2.Substring(shortestLength);
var stringBuilder = new StringBuilder();
for (int i = 0; i < Math.Max(s1.Length, s2.Length); i++)
{
if (i < s1.Length)
stringBuilder.Append(s1[i]);
if (i < s2.Length)
stringBuilder.Append(s2[i]);
}
string result = stringBuilder.ToString();
In JavaScript, when working with strings, you are also working with arrays, so it will be easier. Also + will concatenate for you. Replace string indexing with charAt if you want IE7- support.
Here is the fiddle:
http://jsfiddle.net/z6XLh/1
var s1 = "Mark";
var s2 = "ZuckerFace";
var out ='';
var l = s1.length > s2.length ? s1.length : s2.length
for(var i = 0; i < l; i++) {
if(s1[i]) {
out += s1[i];
}
if(s2[i]){
out += s2[i];
}
}
console.log(out);
static string Join(string a, string b)
{
string returnVal = "";
int length = Math.Min(a.Length, b.Length);
for (int i = 0; i < length; i++)
returnVal += "" + a[i] + b[i];
if (a.Length > length)
returnVal += a.Substring(length);
else if(b.Length > length)
returnVal += b.Substring(length);
return returnVal;
}
Could possibly be improved through stringbuilder
Just for the sake of curiosity, here's an unreadable one-liner (which I have nevertheless split over multiple lines ;))
This uses the fact that padding a string to a certain length does nothing if the string is already at least that length. That means padding each string to the length of the other string will have the result of padding out with spaces the shorter one to the length of the longer one.
Then we use .Zip() to concatenate each of the pairs of characters into a string.
Then we call string.Concat(IEnumerable<string>) to concatenate the zipped strings into a single string.
Finally, we remove the extra padding spaces we introduced earlier by using string.Replace().
var result = string.Concat
(
s1.PadRight(s2.Length)
.Zip
(
s2.PadRight(s1.Length),
(a,b)=>string.Concat(a,b)
)
).Replace(" ", null);
On one line [insert Coding Horror icon here]:
var result = string.Concat(s1.PadRight(s2.Length).Zip(s2.PadRight(s1.Length), (a,b)=>string.Concat(a,b))).Replace(" ", null);
Just off the top of my head, this is how I might do it.
var s1Length = s1.Length;
var s2Length = s2.Length;
var count = 0;
var o = "";
while (s1Length + s2Length > 0) {
if (s1Length > 0) {
s1Length--;
o += s1[count];
}
if (s2Length > 0) {
s2Length--;
o += s2[count];
}
count++;
}
Here's another one-liner:
var s1 = "Mark";
var s2 = "Zukerberg";
var result = string.Join("",
Enumerable.Range(0, s1.Length).ToDictionary(x => x * 2, x => s1[x])
.Concat(Enumerable.Range(0, s2.Length).ToDictionary(x => x * 2+1, x => s2[x]))
.OrderBy(d => d.Key).Select(d => d.Value));
Basically, this converts both strings into dictionaries with keys that will get the resulting string to order itself correctly. The Enumerable range is used to associate an index with each letter in the string. When we store the dictionaries, it multiplies the index on s1 by 2, resulting in <0,M>,<2,a>,<4,r>,<6,k>, and multiplies s2 by 2 then adds 1, resulting in <1,Z>,<3,u>,<5,k>, etc.
Once we have these dictionaries, we combine them with the .Concat and sort them with the .OrderBy,which gives us <0,M>,<1,Z>,<2,a>,<3,u>,... Then we just dump them into the final string with the string.join at the beginning.
Ok, this is the *second shortest solution I could come up with:
public string zip(string s1, string s2)
{
return (string.IsNullOrWhiteSpace(s1+s2))
? (s1[0] + "" + s2[0] + zip(s1.Substring(1) + " ", s2.Substring(1) + " ")).Replace(" ", null)
: "";
}
var result = zip("mark","zukerberg");
Whoops! My original shortest was the same as mark's above...so, second shortest i could come up with! I had hoped I could really trim it down with the recursion, but not so much.
var sWordOne = "mark";// ABCDEF
var sWordTwo = "zukerberg";// 123
var result = (sWordOne.Length > sWordTwo.Length) ? zip(sWordOne, sWordTwo) : zip(sWordTwo, sWordOne);
//result = "zmuakrekrberg"
static string zip(string sBiggerWord, string sSmallerWord)
{
if (sBiggerWord.Length < sSmallerWord.Length) return string.Empty;// Invalid
if (sSmallerWord.Length == 0) sSmallerWord = " ";
return string.IsNullOrEmpty(sBiggerWord) ? string.Empty : (sBiggerWord[0] + "" + sSmallerWord[0] + zip(sBiggerWord.Substring(1),sSmallerWord.Substring(1))).Replace(" ","");
}
A simple alternative without Linq witchcraft:
string Merge(string one, string two)
{
var buffer = new char[one.Length + two.Length];
var length = Math.Max(one.Length, two.Length);
var index = 0;
for (var i = 0; i < length; i ++)
{
if (i < one.Length) buffer[index++] = one[i];
if (i < two.Length) buffer[index++] = two[i];
}
return new string(buffer);
}

Split a long string to a customized string

Hello I have a long string. I want to split it as some kind of format that has many return carrages.
Each line has 5 short words.
Ex.
string input="'250.0','250.00','250.01','250.02','250.03','250.1','250.10','250.11','250.12','250.13','250.2','250.20','250.21','250.22','250.23','250.3','250.30','250.31','250.32','250.33','250.4','250.40','250.41','250.42','250.43','250.5','250.50','250.51','250.52','250.53','250.6','250.60','250.61','250.62','250.63','250.7','250.70','250.71','250.72','250.73','250.8','250.80','250.81','250.82','250.83','250.9','250.90','250.91','250.92','250.93','357.2','357.20','362.01','362.02','362.03','362.04','362.05','362.06','362.07','366.41','648.0','648.00','648.01','648.02','648.03','648.04'";
It has 66 short words.
string output = "'250.0','250.00','250.01','250.02','250.03',
'250.1','250.10','250.11','250.12','250.13',
'250.2','250.20','250.21','250.22','250.23',
'250.3','250.30','250.31','250.32','250.33',
'250.4','250.40','250.41','250.42','250.43',
'250.5','250.50','250.51','250.52','250.53',
'250.6','250.60','250.61','250.62','250.63',
'250.7','250.70','250.71','250.72','250.73',
'250.8','250.80','250.81','250.82','250.83',
'250.9','250.90','250.91','250.92','250.93',
'357.2','357.20','362.01','362.02','362.03',
'362.04','362.05','362.06','362.07','366.41',
'648.0','648.00','648.01','648.02','648.03',
'648.04'";
I thought that I have to count char ',' in the string first such as in the example. But it could be kind of clumsy.
Thanks for advice.
If i've understood you correctly you want to
split those words by comma
group the result into lines where each line contains 5 words
build a string with Environment.NewLine as separator
string input = "'250.0','250.00','250.01','250.02','250.03','250.1','250.10','250.11','250.12','250.13','250.2','250.20','250.21','250.22','250.23','250.3','250.30','250.31','250.32','250.33','250.4','250.40','250.41','250.42','250.43','250.5','250.50','250.51','250.52','250.53','250.6','250.60','250.61','250.62','250.63','250.7','250.70','250.71','250.72','250.73','250.8','250.80','250.81','250.82','250.83','250.9','250.90','250.91','250.92','250.93','357.2','357.20','362.01','362.02','362.03','362.04','362.05','362.06','362.07','366.41','648.0','648.00','648.01','648.02','648.03','648.04'";
int groupCount = 5;
var linesGroups = input.Split(',')
.Select((s, index) => new { str = s, Position = index / groupCount, Index = index })
.GroupBy(x => x.Position);
StringBuilder outputBuilder = new StringBuilder();
foreach (var grp in linesGroups)
{
outputBuilder.AppendLine(String.Join(",", grp.Select(x => x.str)));
}
String output = outputBuilder.ToString();
Edit: The result is:
'250.0','250.00','250.01','250.02','250.03'
'250.1','250.10','250.11','250.12','250.13'
'250.2','250.20','250.21','250.22','250.23'
'250.3','250.30','250.31','250.32','250.33'
'250.4','250.40','250.41','250.42','250.43'
'250.5','250.50','250.51','250.52','250.53'
'250.6','250.60','250.61','250.62','250.63'
'250.7','250.70','250.71','250.72','250.73'
'250.8','250.80','250.81','250.82','250.83'
'250.9','250.90','250.91','250.92','250.93'
'357.2','357.20','362.01','362.02','362.03'
'362.04','362.05','362.06','362.07','366.41'
'648.0','648.00','648.01','648.02','648.03'
'648.04'
If you want to append every line with a comma(like in your example):
foreach (var grp in linesGroups)
{
outputBuilder.AppendLine(String.Join(",", grp.Select(x => x.str)) + ",");
}
// remove last comma + Environment.NewLine
outputBuilder.Length -= ( 1 + Environment.NewLine.Length );
How about this solution:
private static IEnumerable<string> SplitLongString(string input, char separator, int groupSize)
{
int indexCurrent = 0;
int indexLastOccurence = 0;
int separatorCounter = 0;
foreach (var character in input)
{
indexCurrent++;
if (character == separator)
{
separatorCounter++;
if (separatorCounter % groupSize == 0)
{
yield return input.Substring(indexLastOccurence, indexCurrent - indexLastOccurence);
indexLastOccurence = indexCurrent;
}
}
}
if (indexCurrent != indexLastOccurence)
{
yield return input.Substring(indexLastOccurence, indexCurrent - indexLastOccurence);
}
}
And you would call it:
var result = SplitLongString(input, ',', 5);
foreach (var row in result)
{
Console.WriteLine(row);
}
The simplest way would be to follow the approach from #Tim's deleted (edit: not deleted any more) answer:
Split the string into parts by comma (using string.Split)
Rearrange the obtained parts in any way you need: for example, packing by 5 in a line.
Something like that (not tested):
Console.WriteLine("string output =");
var parts = sourceString.Split(',');
int i = 0;
for (; i < parts.Length; i++)
{
if (i % 5 == 0)
Console.Write(' "');
Console.Write(parts[i]);
Console.Write(',');
if (i % 5 == 4 && i != parts.Length - 1)
Console.WriteLine('" +');
}
Console.WriteLine('";');
var input="'250.0','250.00','250.01','250.02','250.03','250.1','250.10','250.11','250.12','250.13','250.2','250.20','250.21','250.22','250.23','250.3','250.30','250.31','250.32','250.33','250.4','250.40','250.41','250.42','250.43','250.5','250.50','250.51','250.52','250.53','250.6','250.60','250.61','250.62','250.63','250.7','250.70','250.71','250.72','250.73','250.8','250.80','250.81','250.82','250.83','250.9','250.90','250.91','250.92','250.93','357.2','357.20','362.01','362.02','362.03','362.04','362.05','362.06','362.07','366.41','648.0','648.00','648.01','648.02','648.03','648.04'";
var wordsArray = input.Split(',');
var sbOutput = new StringBuilder();
for (int i = 1; i < wordsArray.Length +1; i++)
{
sbOutput.AppendFormat("{0},", wordsArray[i-1]);
if(i % 5 == 0)
sbOutput.AppendLine();
}
var output = sbOutput.ToString();
Do something like this:
string[] words = input.Split(',');
int wordsInString = words.Length;

Categories