Generating values in a collection following a pattern in C# - c#

I have following list of strings of tags:
List<string> Tags = new List<string> { "EmplId", "EmpName", "EmpAddress" };
I want to generate tags as following pattern:
EmplId1
EmpName1
EmpAddress1
EmplId2
EmpName2
EmpAddress2
EmplId3
EmpName3
EmpAddress3
I have written the following code:
List<string> Tags = new List<string> { "EmplId", "EmpName", "EmpAddress" };
foreach (var item in Tags)
{
for (int i = 1; i < 4; i++)
{
Console.WriteLine(item+i);
}
}
Console.ReadLine();
But this code generating an output as following:
EmplId1
EmplId2
EmplId3
EmpName1
EmpName2
EmpName3
EmpAddress1
EmpAddress2
EmpAddress3
How I can get the desired output ?

The inner loop really needs to be the outer loop. You want to iterate numbers then values
for (int i = 1; i < 4; i++)
{
foreach (var item in Tags)
{
Console.WriteLine(item+i);
}
}

You can generate items with Linq:
var items = from i in Enumerable.Range(1, 4)
from t in Tags
select t + i;
Produces following items:
EmplId1
EmpName1
EmpAddress1
EmplId2
EmpName2
EmpAddress2
EmplId3
EmpName3
EmpAddress3
EmplId4
EmpName4
EmpAddress4
Without Linq you should do as Jared pointed - swap loops.

Related

Creating a new list by incrementing two separate lists

I'm trying to create a new list from two separate lists like so:
List 1 (sCat) = MD0, MD1, MD3, MD4
List 2 (sLev) = 01, 02, 03, R
Output-->
MD0-01
MD0-02
MD0-03
MD0-R
MD1-01
MD1-02
MD1-03
MD1-R
MD3-01
MD3-02
MD3-03
MD3-R
etc...
I would like to know if there is a function that would produce the results above. Ultimately, I would like the user to provide List 2 and have that information added to List 1 and stored as a new list that I could call later.
enter code here
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static void Main(string[] args)
{
List<string> sCat = new List<string>();
// add Categories for the Sheets
sCat.Add("MD0");
sCat.Add("MD1");
sCat.Add("MD3");
List<string> sLev = new List<string>();
// add Levels for the Project
sLev.Add("01");
sLev.Add("02");
sLev.Add("03");
sLev.Add("R");
for (int i = 0; i < sCat.Count; i++)
{
// I am getting stuck here.
// I don't know how to take one item from the sCat list and
// add it to the sLev List incrementally.
Console.WriteLine(sCat[i],i);
}
Console.ReadLine();
}
}
Combine the values of all the elements selected from the first collection with the elements contained in the other collection:
var combined = sCat.SelectMany(s => sLev.Select(s1 => $"{s}-{s1}")).ToList();
Which is like iterating the two collections in a nested for/foreach loop, adding each combined element to a new List<string>:
List<string> combined = new List<string>();
foreach (var s1 in sCat)
foreach (var s2 in sLev) {
combined.Add(s1 + "-" + s2);
}
You can replace your for loop with following:
foreach(var sCatValue in sCat)
{
foreach(var sLevValue in sLev)
{
Console.WriteLine($"{sCatValue}-{sLevValue}");
}
}
private static void Main()
{
List<string> sCat = new List<string>();
// add Categories for the Sheets
sCat.Add("MD0");
sCat.Add("MD1");
sCat.Add("MD3");
List<string> sLev = new List<string>();
// add Levels for the Project
sLev.Add("01");
sLev.Add("02");
sLev.Add("03");
sLev.Add("R");
string dash = "-";
List<string> newList = new List<string>();
for (int i = 0; i < sCat.Count; i++)
{
for (int j = 0; j < sLev.Count; j++)
{
newList.Add(sCat[i] + dash + sLev[j]);
}
}
foreach (var item in newList)
{
Console.WriteLine(item);
}
Console.ReadLine();
}

Process a list while appending it in C#?

Is there some kind of list like collection I can use in .NET to allow me to append to it while iterating?
var ls = new List<int>();
ls.Add(5);
foreach (var v in ls) {
if (v > 0)
ls.Add(v - 1);
}
//Alternative
var e = ls.GetEnumerator();
while (e.MoveNext()) {
if (e.Current > 0)
ls.Add(e.Current - 1);
}
To mimic such foreach loop, I suggest using for loop, while iterating backward:
for (int i = ls.Count - 1; i >= 0; --i) {
var v = ls[i];
//TODO: put relevant code from existing foreach loop here
}
Or if you have to loop forward
int n = ls.Count; // we don't want iterate the items appended
for (int i = 0; i < n; ++i) {
var v = ls[i];
//TODO: put relevant code from existing foreach loop here
}
In case you do want itetate appended items (see comments below) standard for loop is enough:
for (int i = 0; i < ls.count; ++i) {
var v = ls[i];
//TODO: put relevant code from existing foreach loop here
}
Finally, you can iterate a copy of the original list:
foreach (var v in ls.ToList()) { // ls.ToList() is a shallow copy of the ls
...
}
Using for would allow this. foreach does not allow for the modification of a enumerable while iterating it.
var ls = new List<int>();
ls.Add(5);
for (int i = 0; i < ls.Count; i++) {
var v = ls[i];
if (v > 0)
ls.Add(v - 1);
}
foreach (var v in ls) {
Console.WriteLine(v.ToString());
}
Output:
5
4
3
2
1
0
I don't think that you can directly append to any iterable object while iterating.
But what you can do is create a temporary list like that:
var ls = new List<int>();
ls.Add(5);
var tempList = new List<int>();
foreach(var v in ls){
if(v>0)
tempList.Add(v-1);
}
//Update list
foreach(var v in tempList){
ls.Add(v);
}
//Dont forget to clear the tempList if you need it again !
To include the new additions in the iterations use the index:
for (int i = 0; i < ls.Count; i++)
{
if (ls[i] > 0) ls.Add(ls[i] - 1);
}
To exclude them (and also keep the foreach) use a temporary copy of the List:
foreach (int i in ls.ToList())
{
if (i > 0) ls.Add(i - 1);
}
IMO; This seems to need a recursive solution instead; something like this:
public List<int> AddByReduceNo(List<int> list, int no)
{
if (no > 0)
{
list.Add(no - 1);
return AddByReduceNo(list, no - 1);
}
return list;
}
That after ls.Add(5) use ls = AddByReduceNo(ls, 5);,
or just use ls = AddByReduceNo(ls, 6);
You can have a look at the BlockingCollection class, and see if it fits your needs.
This class has a method GetConsumingEnumerable which gives you an IEnumerable that allows you to iterate over the items that were in the collection at that specific point in time. In the meanwhile, another thread can continue adding items to the collection.
This is especially usefull if you have a consumer and a producer thread.

How do I make the foreach instruction iterate in 2 places?

how do I make the foreach instruction iterate both in the "files" variable and in the "names" array?
var files = Directory.GetFiles(#".\GalleryImages");
string[] names = new string[8] { "Matt", "Joanne", "Robert","Andrei","Mihai","Radu","Ionica","Vasile"};
I've tried 2 options.. the first one gives me lots of errors and the second one displays 8 images of each kind
foreach(var file in files,var i in names)
{
//Do stuff
}
and
foreach(var file in files)
{
foreach (var i in names)
{
//Do stuff
}
}
You can try using the Zip Extension method of LINQ:
int[] numbers = { 1, 2, 3, 4 };
string[] words = { "one", "two", "three" };
var numbersAndWords = numbers.Zip(words, (first, second) => first + " " + second);
foreach (var item in numbersAndWords)
Console.WriteLine(item);
Would look something like this:
var files = Directory.GetFiles(#".\GalleryImages");
string[] names = new string[] { "Matt", "Joanne", "Robert", "Andrei", "Mihai","Radu","Ionica","Vasile"};
var zipped = files.Zip(names, (f, n) => new { File = f, Name = n });
foreach(var fn in zipped)
Console.WriteLine(fn.File + " " + fn.Name);
But I haven't tested this one.
It's not clear what you're asking. But, you can't iterate two iterators with foreach; but you can increment another variable in the foreach body:
int i = 0;
foreach(var file in files)
{
var name = names[i++];
// TODO: do something with name and file
}
This, of course, assumes that files and names are of the same length.
You can't. Use a for loop instead.
for(int i = 0; i < files.Length; i++)
{
var file = files[i];
var name = names[i];
}
If the both array have the same length this should work.
You have two options here; the first works if you are iterating over something that has an indexer, like an array or List, in which case use a simple for loop and access things by index:
for (int i = 0; i < files.Length && i < names.Length; i++)
{
var file = files[i];
var name = names[i];
// Do stuff with names.
}
If you have a collection that doesn't have an indexer, e.g. you just have an IEnumerable and you don't know what it is, you can use the IEnumerable interface directly. Behind the scenes, that's all foreach is doing, it just hides the slightly messier syntax. That would look like:
var filesEnum = files.GetEnumerator();
var namesEnum = names.GetEnumerator();
while (filesEnum.MoveNext() && namesEnum.MoveNext())
{
var file = filesEnum.Current;
var name = namesEnum.Current;
// Do stuff with files and names.
}
Both of these assume that both collections have the same number of items. The for loop will only iterate as many times as the smaller one, and the smaller enumerator will return false from MoveNext when it runs out of items. If one collection is bigger than the other, the 'extra' items won't get processed, and you'll need to figure out what to do with them.
I guess the files array and the names array have the same indices.
When this is the case AND you always want the same index at one time you do this:
for (int key = 0; key < files.Length; ++key)
{
// access names[key] and files[key] here
}
You can try something like this:
var pairs = files.Zip(names, (f,n) => new {File=f, Name=n});
foreach (var item in pairs)
{
Console.Write(item.File);
Console.Write(item.Name);
}

Get first value in CSV column without duplicates

I am getting a list of items from a csv file via a Web Api using this code:
private List<Item> items = new List<Item>();
public ItemRepository()
{
string filename = HttpRuntime.AppDomainAppPath + "App_Data\\items.csv";
var lines = File.ReadAllLines(filename).Skip(1).ToList();
for (int i = 0; i < lines.Count; i++)
{
var line = lines[i];
var columns = line.Split('$');
//get rid of newline characters in the middle of data lines
while (columns.Length < 9)
{
i += 1;
line = line.Replace("\n", " ") + lines[i];
columns = line.Split('$');
}
//Remove Starting and Trailing open quotes from fields
columns = columns.Select(c => { if (string.IsNullOrEmpty(c) == false) { return c.Substring(1, c.Length - 2); } return string.Empty; }).ToArray();
var temp = columns[5].Split('|', '>');
items.Add(new Item()
{
Id = int.Parse(columns[0]),
Name = temp[0],
Description = columns[2],
Photo = columns[7]
});
}
}
The Name attribute of the item list must come from column whose structure is as follows:
Groups>Subgroup>item
Therefore I use var temp = columns[5].Split('|', '>'); in my code to get the first element of the column before the ">", which in the above case is Groups. And this works fine.
However, I a getting many duplicates in the result. This is because other items in the column may be:
(These are some of the entries in my csv column 9)
Groups>Subgroup2>item2, Groups>Subgroup3>item4, Groups>Subgroup4>item9
All start with Groups, but I only want to get Groups once.
As it is I get a long list of Groups. How do I stop the duplicates?
I want that if an Item in the list is returned with the Name "Groups", that no other item with that name would be returned. How do I make this check and implement it?
If you are successfully getting the list of groups, take that list of groups and use LINQ:
var undupedList = dupedList
.Distinct();
Update: The reason distinct did not work is because your code is requesting not just Name, but also, Description, etc...If you only ask for Name, Distinct() will work.
Update 2: Try this:
//Check whether already exists
if((var match = items.Where(q=>q.Name == temp[0])).Count==0)
{
items.add(...);
}
How about using a List to store Item.Name?
Then check List.Contains() before calling items.Add()
Simple, only 3 lines of code, and it works.
IList<string> listNames = new List();
//
for (int i = 0; i < lines.Count; i++)
{
//
var temp = columns[5].Split('|', '>');
if (!listNames.Contains(temp[0]))
{
listNames.Add(temp[0]);
items.Add(new Item()
{
//
});
}
}

Improve the code for performance?

I wrote the follow c# codes to generate a set of numbers and then compare with another set of numbers to remove the unwanted numbers.
But its taking too long at run time to complete the process. Following is the code behind file.
The numbers it has to generate is like 7 figures large and the numbers list which I use it as to remove is around 700 numbers.
Is there a way to improve the run time performance?
string[] strAry = txtNumbersToBeExc.Text.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
int[] intAry = new int[strAry.Length];
List<int> intList = new List<int>();
for (int i = 0; i < strAry.Length; i++)
{
intList.Add(int.Parse(strAry[i]));
}
List<int> genList = new List<int>();
for (int i = int.Parse(txtStartSeed.Text); i <= int.Parse(txtEndSeed.Text); i++)
{
genList.Add(i);
}
lblStatus.Text += "Generated: " + genList.Capacity;
var finalvar = from s in genList where !intList.Contains(s) select s;
List<int> finalList = finalvar.ToList();
foreach (var item in finalList)
{
txtGeneratedNum.Text += "959" + item + "\n";
}
First thing to do is grab a profiler and see which area of your code is taking too long to run, try http://www.jetbrains.com/profiler/ or http://www.red-gate.com/products/dotnet-development/ants-performance-profiler/.
You should never start performance tuning until you know for sure where the problem is.
If the problem is in the linq query than you could try sorting the intlist and doing a binary search for each item to remove, though you can probably get a similar behavour with the right linq query.
string numbersStr = txtNumbersToBeExc.Text;
string startSeedStr = txtStartSeed.Text;
string endSeedStr = txtEndSeed.Text;
//next, the input type actually is of type int, we should test if the strings are ok ( they do represent ints)
var intAry = numbersStr.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries).Select(s=>Int32.Parse(s));
int startSeed = Int32.Parse(startSeedStr);
int endSeed = Int32.Parse(endSeedStr);
/*FROM HERE*/
// using Enumerable.Range
var genList = Enumerable.Range(startSeed, endSeed - startSeed + 1);
// we can use linq except
var finalList = genList.Except(intAry);
// do you need a string, for 700 concatenations I would suggest StringBuilder
var sb = new StringBuilder();
foreach ( var item in finalList)
{
sb.AppendLine(string.Concat("959",item.ToString()));
}
var finalString = sb.ToString();
/*TO HERE, refactor it into a method or class*/
txtGeneratedNum.Text = finalString;
They key point here is that String is a immutable class, so the "+" operation between two strings will create another string. StringBuilder it doesn't do this. On your situation it really doesn't matter if you're using for loops, foreach loops, linq fancy functions to accomplish the exclusion. The performance hurt was because of the string concatenations. I'm trusting more the System.Linq functions because they are already tested for performance.
Change intList from a List to a HashSet - gives much better performance when determining if an entry is present.
Consider using Linq's Enumerable.Intersect, especially combined with #1.
Change the block of code that create genList with this:
List<int> genList = new List<int>();
for (int i = int.Parse(txtStartSeed.Text); i <= int.Parse(txtEndSeed.Text); i++)
{
if (!intList.Contains(i)) genList.Add(i);
}
and after create txtGeneratedNum looping on genList. This will reduce the number of loop of your implementation.
Why not do the inclusion check when you are parsing the int and just build the result list directley.
There is not much point in iterating over the list twice. In fact, why build the intermediate list at all !?! just write straight to a StringBuilder since a newline delimited string seems to be your goal.
string[] strAry = txtNumbersToBeExc.Text.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
var exclusions = new HashSet<T>();
foreach (string s in txtNumbersToBeExc.Text.Split(new string[] { Environment.NewLine })
{
int value;
if (int.TryParse(s, value)
{
exclusions.Add(value);
}
}
var output = new StringBuilder();
for (int i = int.Parse(txtStartSeed.Text); i <= int.Parse(txtEndSeed.Text); i++)
{
if (!exclusions.Contains(i))
{
output.AppendFormat("959{0}\n", i);
}
}
txtGeneratedNum.Text = output.ToString();

Categories

Resources