I have an ArrayList that contains Strings and also contains other ArrayLists which may contain Strings or even more ArrayLists.
Is there a simple way to extract all the Strings from this multilevel ArrayList?
I'm assuming some recursion is invloved but I haven't been able to get it to work.
public static ArrayList FlattenList(ArrayList list) {
ArrayList l = new ArrayList();
FillList(list, l);
return l;
}
private static void FillList(ArrayList source, ArrayList listToFill) {
foreach (object o in source) {
ArrayList l = o as ArrayList;
if (l != null)
FillList(l, listToFill);
else
listToFill.Add(o);
}
}
As an alternative, if you can use generics and iterator blocs then it becomes possible to have a single method:
public static IEnumerable<string> GetStrings(ArrayList list)
{
foreach(var item in list)
{
var #string = item as string;
if (#string != null)
yield return #string;
var nestedList = item as ArrayList;
if(nestedList == null)
continue;
foreach (var childString in GetStrings(nestedList))
yield return childString;
}
}
Related
I've made a method that compares the files within two file directories and it returns if there are files that are duplicated in the form of a bool. However, would actually want it to return the files name or the index of the file in its array so I can then delete the file so their are no complications when moving files into one directory. I've tried to cast the "==" compare statement to a string hoping it would give the files name but I forgot since its a boolean operation it will only return true or false.
static public string ModFileDupilcate(string[] SimsModDownloadDirectory, string[] SimsModsDirectory)
{
string NoDuplicateMods = "There are no duplicate mods";
foreach (var ModInDownloadDirectory in SimsModDownloadDirectory)
{
foreach (var ModInModsDirectory in SimsModsDirectory)
{
if (ModInDownloadDirectory == ModInModsDirectory)
{
string DuplicateMod = (ModInDownloadDirectory == ModInModsDirectory).ToString();
return DuplicateMod;
}
else
{
return NoDuplicateMods;
}
}
}
return NoDuplicateMods;
}
You can get the indexes of the matching strings with something like this
var result = SimsModDownloadDirectory.Select((x, i) =>
{return (SimsModsDirectory.Contains(x) ? i :-1);})
.Where(x => x != -1);
foreach(int index in result)
Console.WriteLine(index);
The idea is the following:
Enumerate with Select all the strings (x) in the first list with the overload that gives us also the index of the enumerated string (i), if the enumerated string is contained in the second list return its index otherwise return -1. Finally take with Where only the not -1 values extracted by the Select
Of course returning only the names of the duplicates is a lot more simple
var result = SimsModDownloadDirectory.Intersect(SimsModsDirectory);
foreach(string name in result)
Console.WriteLine(name);
These approaches are based on the exact match in case between the two strings to compare. So a string "Steve" will not match a string "steve".
If your requirements are to ignore case in the comparison then you could change to
var result = SimsModDownloadDirectory.Select((x, i) =>
{
return (SimsModsDirectory.Contains(x,
StringComparer.CurrentCultureIgnoreCase) ? i :-1);
}).Where(x => x != -1);
or to
var result = SimsModDownloadDirectory.Intersect(SimsModsDirectory,
StringComparer.CurrentCultureIgnoreCase);
The else in your code is the issue.
Sample code (untested)
static public string ModFileDupilcate(string[] SimsModDownloadDirectory, string[] SimsModsDirectory)
{
string NoDuplicateMods = "There are no duplicate mods";
foreach (var ModInDownloadDirectory in SimsModDownloadDirectory)
{
foreach (var ModInModsDirectory in SimsModsDirectory)
{
if (ModInDownloadDirectory == ModInModsDirectory)
{
return ModInModsDirectory;
}
}
}
return NoDuplicateMods;
}
The above only returns the first duplicate. For all duplicates, you have to maintain a list and return that at the end
static public List<string> ModFileDupilcate(string[] SimsModDownloadDirectory, string[] SimsModsDirectory)
{
var duplicateDirs = new List<string>();
foreach (var ModInDownloadDirectory in SimsModDownloadDirectory)
{
foreach (var ModInModsDirectory in SimsModsDirectory)
{
if (ModInDownloadDirectory == ModInModsDirectory)
{
duplicateDirs.Add(ModInModsDirectory);
}
}
}
return duplicateDirs;
}
I don't exactly know what are you trying to achieve. Your code does not tell us what should the return value be. If you want to tell the caller that "There is/isn't duplicate file names", you can easily return bool. If you want to return the "duplicate file names", you should return string[] or FileInfo[] or IReadOnlyCollection<string> or something similar. The advantage of returning a collection or array, is that the caller can easily see that if there is/isn't any duplicates, by checking the Length/Count of the returned value.
Using nested for loops, has a poor performance of O(n*m). Using a HashSet or LINQ's Intersect method, you can easily achieve the goal in O(n+m):
public static IReadOnlyList<string> FindDuplicateModFiles(string[] SimsModDownloadDirectory, string[] SimsModsDirectory)
{
var set = new HashSet<string>(SimsModDownloadDirectory);
var result = new List<string>();
foreach (string file in SimsModsDirectory)
{
if (set.Contains(file))
result.Add(file);
}
return result.AsReadOnly();
}
Or using LINQ:
public static IEnumerable<string> FindDuplicateModFiles2(string[] SimsModDownloadDirectory, string[] SimsModsDirectory)
{
return SimsModDownloadDirectory.Intersect(SimsModsDirectory);
}
If you want to remove the duplicates from the first collection, the best options is the LINQ's Except method:
public static IEnumerable<string> GetNonDuplicatesInFirst(string[] SimsModDownloadDirectory, string[] SimsModsDirectory)
{
return SimsModDownloadDirectory.Except(SimsModsDirectory);
}
static public IEnumerable<string> ModFileDupilcate(string[] SimsModDownloadDirectory,
string[] SimsModsDirectory)
{
var result = SimsModDownloadDirectory.Select((x, i) =>
SimsModsDirectory.Contains(x) ? x : string.Empty).
Where(x => !string.IsNullOrEmpty(x));
return result;
}
Call method like :
var resultOfDublicateFiles = ModFileDupilcate(SimsModDownloadDirectory,SimsModsDirectory);
Or
public static bool ModFileDupilcate(string[] SimsModDownloadDirectory,
List<string> SimsModsDirectory,out List<string> dublicatedFiles)
{
dublicatedFiles = new List<string>();
foreach (var ModInDownloadDirectory in SimsModDownloadDirectory)
{
foreach (var ModInModsDirectory in SimsModsDirectory)
{
if (ModInDownloadDirectory == ModInModsDirectory)
{
dublicatedFiles.Add(ModInModsDirectory);
}
}
}
return dublicatedFiles.Count > 0;
}
Call method like :
List<string> dublicatedFiles;
bool hasDublicatedFiles= ModFileDupilcate(new string["a","b","c"],new string["b","c","d","f"],out dublicatedFiles);
I have a List<object> which contains strings and even additional lists.
List<object> NewArray = new List<object>();
so basically the list contains a mixture....
As a sanity check, I want to print out the contents to the console. I start by iterating through and test to see whether the element is a list. If it isn't then it will be a string and can be printed to the console. If it is a list, I want to iterate through and print the string contents to the console but with a tab to indent it.
I have this so far:
for (int outer = 0; outer < NewArray.Count; outer++)
{
var innerList = NewArray[outer];
if (innerList.GetType().IsGenericType && innerList.GetType().GetGenericTypeDefinition() == typeof(List<>))
{
for (int inner = 0; inner < innerList.Count; inner++)
{
//print string
}
}
else
{
//print string
}
}
I didn't want to use a foreach loop as I'm not sure the order of the list would be guaranteed and will be in the future adding a increment number (which can be provided by the inner and outer variables).
The issue I am getting is an error here:
inner < innerList.Count
which is:
Operator '<' cannot be applied to operands of type 'int' and 'method group'
What do I need to do to overcome this? I'm not sure it is the most efficient way of achieving the end result but....
static void Main()
{
var randomCrap = new List<Object>
{
1, "two",
new List<object> { 3, 4 },
5, 6,
new List<object> {
new List<object> { 7, 8, "nine" },
},
};
randomCrap.PrintAll();
}
Output:
1
two
3
4
5
6
7
8
nine
Using this:
public static class Extensions
{
public static void PrintAll(this Object root)
{
foreach (var x in root.SelectAll())
{
Console.WriteLine(x);
}
}
public static IEnumerable<Object> SelectAll(this object o)
{
// Thank you, eocron
if (o is String)
{
yield return o;
}
else if (o is IEnumerable)
{
var e = o as IEnumerable;
foreach (var child in e)
{
foreach (var child2 in child.SelectAll())
yield return child2;
}
}
else
{
yield return o;
}
}
}
If you know your object is a List<> of some generic type, you could always cast to IList and loop through it that way.
To paraphrase your code:
if (innerList.GetType().IsGenericType && innerList.GetType().GetGenericTypeDefinition() == typeof(List<>))
{
var list = (IList)innerList;
for (int inner = 0; inner < list.Count; inner++)
{
Console.WriteLine(list[inner].ToString());
//print string
}
}
But in reality, you should be doing what itsme86 said, and making strong types with an overriden ToString() or Display() method.
Here is more simplistic way of doing what you want:
public static void DeepPrint(object obj, int recursionLevel)
{
if(obj == null)
{
//print null
return;
}
var str = obj as string;
if(str != null)
{
//print str
return;
}
var enumer = obj as IEnumerable;
if(enumer != null)
{
foreach(var e in enumer)
{
DeepPrint(e, recursionLevel+1);
}
return;
}
//print obj.ToString();
}
Then call it like this on whatever you desire:
DeepPrint(myObjectOrList, 0);
PS
For those who say about "random crap" - embrace the string.Format(...), embrace serialization in general, embrace WCF and dynamic, and etc. There is many random things in this world, what doesn't really need strong typing. In fact it will just become "crap" if you provide strong typing to some common used functions.
One way you could do it is to check if the object implements ICollection, and if so, iterate over the contents. I created a recursive method to handle cases where a collection contained other collections, which includes an indentAmount argument, so that nested collections are indented by a tab each time they're encountered:
public static void PrintItem(object item, int indentAmount = 0)
{
var indent = new string('\t', indentAmount);
if (item == null) Console.WriteLine($"{indent}<null>");
if (item is ICollection)
{
var innerItems = item as IEnumerable;
Console.WriteLine($"{indent}Collection type encountered:");
indentAmount++;
foreach (var innerItem in innerItems)
{
PrintItem(innerItem, indentAmount);
}
}
else
{
Console.WriteLine($"{indent}{item}");
}
}
Usage
private static void Main()
{
var items = new List<object>
{
"first",
2,
new List<string> {"listFirst", "listSecond"},
new[] {"arrayFirst", "arraySecond"},
new ArrayList {"arrayListFirst", "arrayListSecond"},
"third",
new List<List<string>>
{
new List<string> {"nestedList1First", "nestedList1Second"},
new List<string> {"nestedList2First", "nestedList2Second"}
},
4f,
new object[] {5d, "six", new List<object>{"seven", 8} },
9,
"ten"
};
PrintItem(items);
Console.WriteLine("\nDone!\nPress any key to exit...");
Console.ReadKey();
}
Output
I'm using C# and framework 4.0.
I have a list of type string and another list of type class T;
How can I compare List with a List and save the difference?
private void simpleButton_Compare_Click(object sender, EventArgs e)
{
try
{
bool Is_Egal = true;
int i = 0;
foreach (string Od_Scan in Ordre_Scan)
{
if (!Outils.Get_Ordre_Donne()[i].NoOrdre.Contains(Od_Scan) && !String.IsNullOrWhiteSpace(Od_Scan))
{
Is_Egal = false;
Temp_Od_Scan.Add(Od_Scan);
}
i++;
}
foreach (Pers_Compare Od_Done in Outils.Get_Ordre_Donne())
{
if (!Ordre_Scan.Contains(Od_Done.NoOrdre) && !String.IsNullOrWhiteSpace(Od_Done.NoOrdre))
{
Is_Egal = false;
Temp_Od_Donne.Add(Od_Done);
}
else
{
Temp_Od_Donne_Egal.Add(Od_Done);
}
}
if (Is_Egal)
{
MessageBox.Show("égalité");
}
else
{
MessageBox.Show("PAS égalité");
}
}
catch (Exception excThrown)
{
MessageBox.Show(excThrown.Message);
}
}
and the data :
List<string> Ordre_Scan= new List<string> { "azer","qsdf"};
Pers_Compare obj = new Pers_Compare();
obj.Nolv = 1;
obj.Noordre = "qsdf"
Pers_Compare obj2 = new Pers_Compare();
obj2.Nolv = 1;
obj2.Noordre = "wxcv"
List<Pers_Compare> Ordre_Donne = new List<Pers_Compare>();
Ordre_Donne.add(obj);
Ordre_Donne.add(obj2);
And I want to save the data in Ordre_Donne but not in Od_Scan and vice versa.
foreach (string Od_Scan in Temp_Od_Scan)
{
all item that not found in List A
--> wxcv
}
foreach (var Od_Done in Temp_Od_Donne)
{
all item that not found in List B
--> azer
}
The answer given for a slightly different question (comparing a List with another List) seems to me to be a good solution for your issue, they address multiple issues to do with comparisons of lists.
EDIT: However you should be more specific with your requirements i.e. what exactly is a 'difference', e.g. is {1,1,2} and {1,2} the same?
Here is the answer given the most votes... (included here just encase it gets removed for some reason (as per Bob' suggestion))
"
DESCRIPTION:
I need to check that they both have the same elements, regardless of their position within the list. Each MyType object may appear multiple times on a list. Is there a built-in function that checks this? What if I guarantee that each element appears only once in a list?
EDIT: Guys thanks for the answers but I forgot to add something, the number of occurrences of each element should be the same on both lists.
ANSWER:
If you want them to be really equal (i.e. the same items and the same number of each item), I think that the simplest solution is to sort before comparing:
Enumerable.SequenceEqual(list1.OrderBy(t => t), list2.OrderBy(t => t))
Edit:
Here is a solution that performs a bit better (about ten times faster), and only requires IEquatable, not IComparable:
public static bool ScrambledEquals<T>(IEnumerable<T> list1, IEnumerable<T> list2) {
var cnt = new Dictionary<T, int>();
foreach (T s in list1) {
if (cnt.ContainsKey(s)) {
cnt[s]++;
} else {
cnt.Add(s, 1);
}
}
foreach (T s in list2) {
if (cnt.ContainsKey(s)) {
cnt[s]--;
} else {
return false;
}
}
return cnt.Values.All(c => c == 0);
}
Edit 2:
To handle any data type as key (for example nullable types as Frank Tzanabetis pointed out), you can make a version that takes a comparer for the dictionary:
public static bool ScrambledEquals<T>(IEnumerable<T> list1, IEnumerable<T> list2, IEqualityComparer<T> comparer) {
var cnt = new Dictionary<T, int>(comparer);
...
"
var list1 = Ordre_Donne.Where(o => !Ordre_Scan.Any(s => s == o.Noordre));
var list2 = Ordre_Scan.Where(s => !Ordre_Donne.Any(o => o.Noordre == s));
You can either implement IComparable on your Pers_Compare class, which will look something like:
public int CompareTo(string other)
{
return this.Noordre.CompareTo(other);
}
Or, if you don't have control of the data structure, you could do something like
var Temp_Od_Donne = from od in Ordre_Donne
where !Ordre_Scan.Contains(od.Noordre)
select od;
var Temp_Od_Scan = from os in Ordre_Scan
where !Ordre_Donne.Select(od => od.Noordre).Contains(os)
select os;
I would like to to split list into all the cases SubLists using LINQ?
For example :
List contains : {"a", "b", "c"}
I would like to make list of lists where the result is : {"a", "ab", "abc"}
public List<List<Alphabet>> ListofLists (Stack<String> Pile)
{
var listoflists = new List<List<Alphabet>>();
var list = new List<Alphabet>();
foreach (var temp in from value in Pile where value != "#" select new Alphabet(value))
{
list.Add(temp);
listoflists.Add(list);
}
return listoflists;
}
This method will allow you to do this.
IEnumerable<IEnumerable<T>> SublistSplit<T>(this IEnumerable<T> source)
{
if (source == null) return null;
var list = source.ToArray();
for (int i = 0; i < list.Length; i++)
{
yield return new ArraySegment<T>(list, 0, i);
}
}
In case of strings:
IEnumerable<string> SublistSplit<T>(this IEnumerable<string> source)
{
if (source == null) return null;
var sb = new StringBuilder();
foreach (var x in source)
{
sb.Append(x);
yield return sb.ToString();
}
}
If you want to yield the intermediate values of an accumulation you could define your own extension method:
public IEnumerable<TAcc> Scan<T, TAcc>(this IEnumerable<T> seq, TAcc init, Func<T, TAcc, TAcc> acc)
{
TAcc current = init;
foreach(T item in seq)
{
current = acc(item, current);
yield return current;
}
}
then your example would be:
var strings = new[] {"a", "b", "c"}.Scan("", (str, acc) => str + acc);
for lists, you'll have to copy them each time:
List<Alphabet> input = //
List<List<Alphabet>> output = input.Scan(new List<Alphabet>(), (a, acc) => new List<Alphabet(acc) { a }).ToList();
Note that copying the intermediate List<T> instances could be inefficient, so you may want to consider using an immutable structure instead.
I have a requirement where I can get the following in an object -
a type T or List<T>
Converting object into T is easy. How can I convert it to List(by first checking that it can be converted successfully or not), reason I want to convert is to scroll through the list and call tostring on each element.
My actual code -
namespace Generic_Collection_Code
{
class Program
{
public static string DumpObj(object obj)
{
string sTemp = String.Empty;
List<int> ints = obj as List<int>;
if (ints != null)
{
foreach (int i in ints)
sTemp += i.ToString() + ",";
sTemp.Trim(',');
}
else
{
List<string> strings = obj as List<string>;
if (strings != null)
{
foreach (string s in strings)
sTemp += s + ",";
sTemp.Trim(',');
}
else
{
sTemp += obj.ToString();
}
}
return sTemp;
}
static void Main(string[] args)
{
List<int> listInts = new List<int>();
listInts.Add(1);
listInts.Add(2);
listInts.Add(3);
Console.WriteLine("Object1: {0}", DumpObj(listInts));
int i = 90;
Console.WriteLine("Object2 {0}", DumpObj(i));
List<string> listStrings = new List<string>();
listStrings.Add("1");
listStrings.Add("2");
listStrings.Add("3");
Console.WriteLine("Object3: {0}", DumpObj(listStrings));
Console.ReadKey();
}
}
}
The above code works but I know its an ugly way to achieve this. I wanted to ask from community how can I have this function like -
public static string DumpObj<T>(object obj)
{
string sTemp = String.Empty;
List<T> list = obj as List<T>;
if (list != null)
{
foreach (T i in list)
sTemp += i.ToString() + ",";
sTemp.Trim(',');
}
return sTemp;
}
This gives me compilation errors as I have to specify T while calling DumpObj with error as -
Error 1 The type arguments for method 'Generic_Collection_Code.Program.DumpObj(object)' cannot be inferred from the usage. Try specifying the type arguments explicitly. D:\DotNet\Generic_Collection_Code\Generic_Collection_Code\Program.cs 57 47 Generic_Collection_Code
as you can see, obj is an object, i dont know its type while calling dumobj.
I hope I have made myself clear on this one.
I appreciate your time!
Regards
Amit
Say
List<T> genericList = object as List<T>;
if(genericList != null)
{
// Do the loop
}
The "as" keyword verifies that "object" actually "is-a" List< T >. If so, you get a List< T > back from it. If not, you get null.
What is the compilation error you're getting? If T is declared as a generic type parameter in your context then then the only compile-time issue I can see with that statement is the use of the keyword object as a variable name. At any rate, I'd suggest something like this as best expressing your intention:
IEnumerable enumerable = obj as IEnumerable;
if (enumerable != null)
{
foreach (object item in enumerable)
{
sTemp += item.ToString();
}
}
You may also want to consider using a StringBuilder if your list is likely to have a lot of items.
you cant do this
List<T> genericList = (List<T>)object
might be you want
List<T> genericList = (List<T>)obj
where obj is object
How about combining "as" with "is"?
if (object is List<T>)
{
List<T> genericlist = object as List<T>;
// loop list
}
else if (object is T)
{
// do something else
}
How about casting the Object into System.Collections.IList (instead of the Generic version) because Generic list also implement this interface. Then cast each of them into the desired type.
Here is what I am working on..
private static void DataSourcePropertyChanged(DependencyObject sender,
DependencyPropertyChangedEventArgs args) {
BarChart _ = sender as BarChart;
if (args.Property.Equals(BarChart.DataSourceProperty)) {
System.Collections.IList data = (System.Collections.IList)args.NewValue;
if (data == null) return;
foreach (object __ in data) {
IChartDataItem item = __ as IChartDataItem;
BarChartItem bar = new BarChartItem() {
Label = item.Label,
Value = item.Value
};
_._visualCollection.Add(bar);
if (_.MaxData < item.Value)
_.MaxData = item.Value;
}
if (_.Orientation == Orientation.Horizontal)
_.Ratio = _.Width / _.MaxData;
}
}