This question already has answers here:
How do I check for null or empty string for many arguments? - C#
(5 answers)
Closed 2 years ago.
Variables:
private string filePath1 = null;
private string filePath2 = null;
private string filePath3 = null;
private string filePath4 = null;
private string filePath5 = null;
private string filePath6 = null;
private string filePath7 = null;
private string filePath8 = null;
private string filePath9 = null;
private string filePath10 = null;
Current If statement
if (string.IsNullOrEmpty(filePath1))
{
errors.Add("File Not Attached");
}
if (string.IsNullOrEmpty(filePath2))
{
errors.Add("File Not Attached");
}
....
Question:
Instead of having multiple if statements, for each variable. How can I create 1 if statement to go through all these variables?
Something like this:
if (string.IsNullOrEmpty(filePath + range(1 to 10))
{
errors.Add("File Not Attached");
}
You can achieve this using Reflection. This is obviously discouraged for this scenario, as the other answers provide better solutions, just wanted to show you it's doable the way you intended it to be done (which doesn't mean it's the correct way)
public class Test
{
private string filePath1 = null;
private string filePath2 = null;
private string filePath3 = null;
}
Usage:
Test obj = new Test();
//loop through the private fields of our class
foreach (var fld in obj.GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Instance)
.Where(x => x.Name.StartsWith("filePath"))) // filter
{
if (string.IsNullOrEmpty(fld.GetValue(obj) as string))
{
errors.Add("File Not Attached in variable: " + fld.Name);
}
}
In nearly all cases where you're using variables with a differently numbered suffix, you should really be using a collection (array, list, ...). This is one of those cases. I'll be using a list for this answer but any collection will suffice.
private List<string> filePaths = new List<string>()
{
"path1",
"path2",
"path3",
"path4"
};
You can then use a loop to iterate over your list:
foreach (string path in filePaths)
{
if(String.IsNullOrEmpty(path))
errors.Add("File not attached");
}
Create a new arraylist, add all file paths to it (or initialise it with all filepaths) and the loop over the elements in the array (using for-each loop). For each element, check if nullOrEmpty and if yes add to your errors string.
ArrayList arrlist = new ArrayList();
arrList.add(filePath1);
arrList.add(filePath2);
arrList.add(filePath3);
arrList.add(filePath4);
arrList.add(filePath5);
arrList.add(filePath6);
arrList.add(filePath7);
arrList.add(filePath8);
arrList.add(filePath9);
arrList.add(filePath10);
foreach (string element in arrList)
{
if (string.IsNullOrEmpty(element)
{
errors.Add("File Not Attached");
}
}
ps. You might want to print a new line after each error:
errors.Add("File Not Attached\n");
// Create list
List<string> filePaths = new List<string>;
//Add path in list like
filePaths.add(filePath1);
//Check for null path here
foreach (string filepath in filePaths)
{
if (string.IsNullOrEmpty(filepath)
{
errors.Add("File Not Attached");
}
}
In order to treat all strings the same way they have to be in some collection.
using System.Linq;
...
string[] allPaths = new string[10];
// Do something with these ten paths...
if (allPaths.Any(x => string.IsNullOrEmpty(x))
errors.Add("File Not Attached");
As stated every other answers, you should use a collection.
If you really want to stick with fields names, you can use reflection, but I strongly recommend to use collections over reflection :
// using System.Reflection;
// Below code is meant to be used in a method of the class that holds the fields.
for (int i = 1; i <= 10; i++)
{
if (string.IsNullOrEmpty(this.GetType()
.GetField($"filePath{i}",
BindingFlags.NonPublic | BindingFlags.Instance)?
.GetValue(this))
{
errors.Add("File Not Attached");
}
}
If you can make those variable class fields i would vote for Innat3's Answer.
Bu if this is not possible and you can't make those variables class fields then i suggest to you do like following :
class Program
{
static void Main(string[] args)
{
Dictionary<string, int> names = new Dictionary<string,int>();
for (int i = 0; i < 10; i++)
{
names.Add(String.Format("name{0}", i.ToString()), i);
}
var xx1 = names["name1"];
var xx2 = names["name2"];
var xx3 = names["name3"];
}
}
Because in c# we can't compute dynamically variable names.
Hope this helps.
Related
is it possible to include multiple "foreach" statements inside any of the looping constructs like while or for ... i want to open the .wav files from two different directories simultaneously so that i can compare files from both.
here is what i am trying to so but it is certainly wrong.. any help in this regard is appreciated.
string[] fileEntries1 = Directory.GetFiles(folder1, "*.wav");
string[] fileEntries2 = Directory.GetFiles(folder11, "*.wav");
while ( foreach(string fileName1 in fileEntries1) && foreach(string fileName2 in fileEntries2))
Gramatically speaking no. This is because a foreach construct is a statement whereas the tests in a while statement must be expressions.
Your best bet is to nest the foreach blocks:
foreach(string fileName1 in fileEntries1)
{
foreach(string fileName2 in fileEntries2)
I like this kind of statements in one line. So even though most of the answers here are correct, I give you this.
string[] fileEntries1 = Directory.GetFiles(folder1, "*.wav");
string[] fileEntries2 = Directory.GetFiles(folder11, "*.wav");
foreach( var fileExistsInBoth in fileEntries1.Where(fe1 => fileEntries2.Contains(fe1) )
{
/// here you will have the records which exists in both of the lists
}
Something like this since you only need to validate same file names:
IEnumerable<string> fileEntries1 = Directory.GetFiles(folder1, "*.wav").Select(x => Path.GetFileName(x));
IEnumerable<string> fileEntries2 = Directory.GetFiles(folder2, "*.wav").Select(x => Path.GetFileName(x));
IEnumerable<string> filesToIterate = (fileEntries1.Count() > fileEntries2.Count()) ? fileEntries1 : fileEntries2;
IEnumerable<string> filesToValidate = (fileEntries1.Count() < fileEntries2.Count()) ? fileEntries1 : fileEntries2;
// Iterate the bigger collection
foreach (string fileName in filesToIterate)
{
// Find the files in smaller collection
if (filesToValidate.Contains(fileName))
{
// Get actual file and compare
}
else
{
// File does not exist in another list. Handle appropriately
}
}
.Net 2.0 based solution:
List<string> fileEntries1 = new List<string>(Directory.GetFiles(folder1, "*.wav"));
List<string> fileEntries2 = new List<string>(Directory.GetFiles(folder2, "*.wav"));
List<string> filesToIterate = (fileEntries1.Count > fileEntries2.Count) ? fileEntries1 : fileEntries2;
filesToValidate = (fileEntries1.Count < fileEntries2.Count) ? fileEntries1 : fileEntries2;
string iteratorFileName;
string validatorFilePath;
// Iterate the bigger collection
foreach (string fileName in filesToIterate)
{
iteratorFileName = Path.GetFileName(fileName);
// Find the files in smaller collection
if ((validatorFilePath = FindFile(iteratorFileName)) != null)
{
// Compare fileName and validatorFilePath files here
}
else
{
// File does not exist in another list. Handle appropriately
}
}
FindFile method:
static List<string> filesToValidate;
private static string FindFile(string fileToFind)
{
string returnValue = null;
foreach (string filePath in filesToValidate)
{
if (string.Compare(Path.GetFileName(filePath), fileToFind, true) == 0)
{
// Found the file
returnValue = filePath;
break;
}
}
if (returnValue != null)
{
// File was found in smaller list. Remove this file from the list since we do not need to look for it again
filesToValidate.Remove(returnValue);
}
return returnValue;
}
You may or may not choose to make fields and methods static based on your needs.
If you want to iterate all pairs of files in both paths respectively, you can do it as follows.
string[] fileEntries1 = Directory.GetFiles(folder1, "*.wav");
string[] fileEntries2 = Directory.GetFiles(folder11, "*.wav");
foreach(string fileName1 in fileEntries1)
{
foreach(string fileName2 in fileEntries2)
{
// to the actual comparison
}
}
This is what I would suggest, using linq
using System.Linq;
var fileEntries1 = Directory.GetFiles(folder1, "*.wav");
var fileEntries2 = Directory.GetFiles(folder11, "*.wav");
foreach (var entry1 in fileEntries1)
{
var entries = fileEntries2.Where(x => Equals(entry1, x));
if (entries.Any())
{
//We have matches
//entries is a list of matches in fileentries2 for entry1
}
}
If you want to enable both collections "in parallel", then use their iterators like this:
var fileEntriesIterator1 = Directory.EnumerateFiles(folder1, "*.wav").GetEnumerator();
var fileEntriesIterator2 = Directory.EnumerateFiles(folder11, "*.wav").GetEnumerator();
while(fileEntriesIterator1.MoveNext() && fileEntriesIterator2.MoveNext())
{
var file1 = fileEntriesIterator1.Current;
var file2 = fileEntriesIterator2.Current;
}
If one collection is shorter than the other, this loop will end when the shorter collection has no more elements.
In c# I have 2 lists.
First List is like so:
respondent.PrescreenerResponses[i].Response[j] = {[12, some response]}
Second List is like so:
projects.Prescreeners[i].Questions[j] = {Prescreener Questions: Question}
What I want to do is create one list probably like:
Prescreeners[i].Responses[j]
Prescreeners[i].Questions[j]
My code though is somehow wrong:
foreach (var screener in respondent.PreScreenerResponses)
{
var responses = screener;
}
foreach (var screener in project.PreScreeners)
{
var questions = screener;
}
List<string> prescreenerResponses = new List<string>();
prescreenerResponses.Add(questions);
It tells me questions does not exist in the current context. Same goes for responses when I try to use it. I am pretty sure it the wrong data type but not sure what else it would be?
I am pretty sure it the wrong data type
No, it's scope. You declare the var responses and var questions in their respective foreach block. As soon as control leaves those blocks, the variables don't exist anymore.
Declare them first:
IEnumerable<PrescreenerResponses> responses = new List<PrescreenerResponses>;
foreach (...)
{
responses.Add(...)
}
Anyway you can also call AddRange() if you declase responses as List, so you can skip the loop:
responses.AddRange(respondent.PreScreenerResponses);
And it's even more advisable to create a DTO:
class QuestionAndAnswer
{
public PreScreener Question { get; set; }
public PreScreenerResponse Response { get; set; }
}
And use a loop with a counter to fill a List<QuestionAndAnswer>:
var result = new List<QuestionAndAnswer>();
for (int i = 0; i < projects.Prescreeners.Count; i++)
{
result.Add(new QuestionAndAnswer
{
Question = projects.Prescreeners[i],
Answer = respondent.PrescreenerResponses[i],
});
}
Try it like this
List<string> prescreenerResponses = new List<string>();
foreach (var screener in respondent.PreScreenerResponses)
{
var responses = screener;
}
foreach (var screener in project.PreScreeners)
{
prescreenerResponses.Add(screener);
}
You are accessing variable "questions" which is declared outside the scope,
Try this:
List<string> prescreenerResponses = new List<string>();
foreach (var screener in respondent.PreScreenerResponses)
{
var responses = screener;
}
foreach (var screener in project.PreScreeners)
{
var questions = screener;
prescreenerResponses.Add(questions);
}
It´s because 'questions' is declared inside your loop. You could try the code below:
private class QuestionAndResponses
{
public List<Response> Responses {get;set;}
public List<Question> Questions {get;set;}
}
List<QuestionAndResponses> prescreenerResponses = new List<QuestionAndResponses>();
for (var i = 0; i < project.preScreeners.Count(); i++)
{
prescreenerResponses.Add(new QuestionAndResponses ()
{
Responses = new List<Response>(project.preScreenerResponses[i].Response),
Questions = new List<Question>(project.preScreeners[i].Questions)
});
}
I'm posting data to a service that requires that I submit duplicate query string keys (ugly and not specified in any standards).
I'm using WebClient object to build the request. I'd like to keep using it since it is used frequently elsewhere in the project.
When I do this
foreach(var f in formats)
client.QueryString.Add("formats", f);
I get a list &formats=format_1,format_2,format_3 which the service does not support.
Is there a better alternative than this old-school ugliness:
var extraQueryString = string.Empty;
extraQueryString += "?apiKey=" + TRANSCODE_KEY;
extraQueryString += "&fileKey=" + fileKey;
foreach (var f in formats)
extraQueryString += "&formats=" + f;
var response = client.UploadData(TRANSCODE_URI + "task" + extraQueryString , new byte[] { });
The reason for this is because the NameValueCollection separates duplicate keys with commas. You could extend the NameValueCollection and override the Get method and have it return the format you want.
public class DupeNVC: NameValueCollection
{
private string _duplicateKey;
public DupeNVC(string duplicateKey = null)
{
_duplicateKey = duplicateKey;
}
public override string Get(int index)
{
//check if duplicate key has been specified
//if not, then call the default Get implementation
if (!String.IsNullOrEmpty(_duplicateKey))
{
ArrayList list = (ArrayList)base.BaseGet(index);
int num = (list != null) ? list.Count : 0;
if (num == 1)
{
return (string)list[0];
}
if (num > 1)
{
StringBuilder stringBuilder = new StringBuilder((string)list[0]);
for (int i = 1; i < num; i++)
{
//format our string and append the duplicate key specified
stringBuilder.AppendFormat("&{0}=", _duplicateKey);
stringBuilder.Append((string)list[i]);
}
return stringBuilder.ToString();
}
return null;
}
else
return base.Get(index);
}
}
You can use it like a normal NameValueCollection but if you pass in a duplicate strning in the constructor, it will look for that duplicate key and run the modified code above (otherwise it will just use the default base.Get method.
DupeNVC dnvc = new DupeNVC("formats");
foreach(var f in formats)
dnvc.Add("formats", f);
webClient.QueryString = dnvc;
This hasn't been fully tested but it should output the querystring format you want. Of course, this could be extended further by taking a collection of duplicate keys but this was just to give you an idea for your current problem.
Here's my take on this. WebClient essentially works like the ToString method of this class; it gets all the keys and then retrieves the values one at a time, doing a concatenate. So I override the AllKeys to return an array with repeated elements..
For example if a particular key has multiple values:
nvc["hello"] = { "a", "b", "c" }
Then my AllKeys will return an array with "hello" 3 times. WebClient will naively request it 3 times. A Dictionary tracks how many times a "hello" has been requested, and returns a different one each time (pseudo enumerator)
public class ParrotingNameValueCollection : NameValueCollection
{
Dictionary<string, int> _indexTracker = new Dictionary<string, int>();
public override string[] AllKeys
{
get
{
var l = new List<string>();
foreach (var k in base.AllKeys)
{
foreach (var x in (ArrayList)base.BaseGet(k))
l.Add(k);
_indexTracker[k] = 0;
}
return l.ToArray();
}
}
public override string Get(string name)
{
var list = (ArrayList)base.BaseGet(name);
var toReturn = (string)list[_indexTracker[name]];
_indexTracker[name]++;
return toReturn;
}
public override string ToString()
{
string delimiter = String.Empty;
StringBuilder values = new StringBuilder();
foreach (string name in this.AllKeys)
{
values.Append(delimiter);
values.Append((name));
values.Append("=");
values.Append((this[name]));
delimiter = "&";
}
return values.ToString();
}
}
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Remove duplicates from a List<T> in C#
i have a List like below (so big email list):
source list :
item 0 : jumper#yahoo.com|32432
item 1 : goodzila#yahoo.com|32432|test23
item 2 : alibaba#yahoo.com|32432|test65
item 3 : blabla#yahoo.com|32432|test32
the important part of each item is email address and the other parts(separated with pipes are not important) but i want to keep them in final list.
as i said my list is to big and i think it's not recommended to use another list.
how can i remove duplicate emails (entire item) form that list without using LINQ ?
my codes are like below :
private void WorkOnFile(UploadedFile file, string filePath)
{
File.SetAttributes(filePath, FileAttributes.Archive);
FileSecurity fSecurity = File.GetAccessControl(filePath);
fSecurity.AddAccessRule(new FileSystemAccessRule(#"Everyone",
FileSystemRights.FullControl,
AccessControlType.Allow));
File.SetAccessControl(filePath, fSecurity);
string[] lines = File.ReadAllLines(filePath);
List<string> list_lines = new List<string>(lines);
var new_lines = list_lines.Select(line => string.Join("|", line.Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries)));
List<string> new_list_lines = new List<string>(new_lines);
int Duplicate_Count = 0;
RemoveDuplicates(ref new_list_lines, ref Duplicate_Count);
File.WriteAllLines(filePath, new_list_lines.ToArray());
}
private void RemoveDuplicates(ref List<string> list_lines, ref int Duplicate_Count)
{
char[] splitter = { '|' };
list_lines.ForEach(delegate(string line)
{
// ??
});
}
EDIT :
some duplicate email addrresses in that list have different parts ->
what can i do about them :
mean
goodzila#yahoo.com|32432|test23
and
goodzila#yahoo.com|asdsa|324234
Thanks in advance.
say you have a list of possible duplicates:
List<string> emailList ....
Then the unique list is the set of that list:
HashSet<string> unique = new HashSet<string>( emailList )
private void RemoveDuplicates(ref List<string> list_lines, ref int Duplicate_Count)
{
Duplicate_Count = 0;
List<string> list_lines2 = new List<string>();
HashSet<string> hash = new HashSet<string>();
foreach (string line in list_lines)
{
string[] split = line.Split('|');
string firstPart = split.Length > 0 ? split[0] : string.Empty;
if (hash.Add(firstPart))
{
list_lines2.Add(line);
}
else
{
Duplicate_Count++;
}
}
list_lines = list_lines2;
}
The easiest thing to do is to iterate through the lines in the file and add them to a HashSet. HashSets won't insert the duplicate entries and it won't generate an exception either. At the end you'll have a unique list of items and no exceptions will be generated for any duplicates.
1 - Get rid of your pipe separated string (create an dto class corresponding to the data it's representing)
2 - which rule do you want to apply to select two object with the same id ?
Or maybe this code can be useful for you :)
It's using the same method as the one in #xanatos answer
string[] lines= File.ReadAllLines(filePath);
Dictionary<string, string> items;
foreach (var line in lines )
{
var key = line.Split('|').ElementAt(0);
if (!items.ContainsKey(key))
items.Add(key, line);
}
List<string> list_lines = items.Values.ToList();
First, I suggest to you load the file via stream.
Then, create a type that represent your rows and load them into a HashSet(for
performance considerations).
Look (Ive removed some of your code to make it simple):
public struct LineType
{
public string Email { get; set; }
public string Others { get; set; }
public override bool Equals(object obj)
{
return this.Email.Equals(((LineType)obj).Email);
}
}
private static void WorkOnFile(string filePath)
{
StreamReader stream = File.OpenText(filePath);
HashSet<LineType> hashSet = new HashSet<LineType>();
while (true)
{
string line = stream.ReadLine();
if (line == null)
break;
string new_line = string.Join("|", line.Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries));
LineType lineType = new LineType()
{
Email = new_line.Split('|')[3],
Others = new_line
};
if (!hashSet.Contains(lineType))
hashSet.Add(lineType);
}
}
Is there a way to dynamically name variables?
What I need to do is take a list of variable names from an input file and create variables with those names. Is this possible?
Something like:
Variable <dynamic name of variable here> = new Variable(input);
Assume that I already have the Variable class taken care of, and the name of the variable is contain in a string called strLine.
Use a Dictionary<string, Variable>.
e.g.
var vartable = new Dictionary<string, Variable>();
vartable[strLine] = new Variable(input);
C# 4.0, using the dynamic objects:
dynamic d = new ExpandoObject();
((IDictionary<string, object>)d)["MyProperty"] = 5;
int val = d.MyProperty; // 5
No, but you could use a Dictionary<string, Variable>, and then you can refer to each variable by its quoted name.
You can't do that, but what you're looking to do is begging for a Dictionary use:
Dictionary<object, object> d = new Dictionary<string, object>();
d.Add("Variable1", value1);
d.Add("Variable2", value2);
d.Add("Variable3", value3);
try this one,user json to serialize and deserialize:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web.Script.Serialization;
namespace ConsoleApplication1
{
public class Program
{
static void Main(string[] args)
{
object newobj = new object();
for (int i = 0; i < 10; i++)
{
List<int> temp = new List<int>();
temp.Add(i);
temp.Add(i + 1);
newobj = newobj.AddNewField("item_" + i.ToString(), temp.ToArray());
}
}
}
public static class DynamicExtention
{
public static object AddNewField(this object obj, string key, object value)
{
JavaScriptSerializer js = new JavaScriptSerializer();
string data = js.Serialize(obj);
string newPrametr = "\"" + key + "\":" + js.Serialize(value);
if (data.Length == 2)
{
data = data.Insert(1, newPrametr);
}
else
{
data = data.Insert(data.Length-1, ","+newPrametr);
}
return js.DeserializeObject(data);
}
}
}
Variable names should be known at compile time. If you intend to populate those names dynamically at runtime you could use a List<T>.
var variables = List<Variable>();
variables.Add(new Variable { Name = input1 });
variables.Add(new Variable { Name = input2 });
...
No. You can load them into a Dictionary object, however. This allows you to still reference them (somewhat) using a name, which is a bit easier than using an Index, as you would with an Array or ArrayList.
I would use some sort of keyed collection, like a hashtable, dictionary, or list of structures with the name. You can then refer to the variable by name:
var value = variableDictionary["MyVariableName"];
var value = variableHashtable["MyVariableName"];
var value = variableList.First(x=>x.Name == "MyVariableName");
There is no other way to dynamically "name" a variable.
No. Use an array, otherwise you can't do this.