Related
So I have programmed for about 6 months now and since it is break I am bored and playing around with stuff. I was going over the concepts of arrays again and made an array that produced 50 randoms numbers and used an insertion sort on them to put them in order. Then I started messing with string arrays. string[] array = new string[] { "Ultima Online", "Everquest", "Baldur's Gate", "Diablo I"}; is my string array. To get it to display the entire string I used a linked list (which I am not sure if I had to, my attempts to casting it didn't work i.e Convert.ToString(array) failed). so I have this
static void Main(string[] args)
{
string[] array = new string[] { "Ultima Online", "Everquest", "Baldur's Gate", "Diablo I"};
List<string> list = new List<string>(array);
for (int i = 0; i < list.Count; i++)
{
Console.WriteLine(list[i]);
}
}
This displays the entire array. Then I thought what if I want to remove or add something to it. So, I placed list.RemoveAt(2); above my Console.Writeline(list[i]);
But this did something I didn't expect. It removes everything at and after the 2nd spot in my list. So I assumed RemoveAt() is going to always remove everything after that value that is indicated.
However, list.Remove(2); gives my errors. 1. Argument 1: cannot convert from 'int' to 'string' and then 2. The best overloaded method match...etc.. argument.
Again my casting didn't work. So, I reverted to RemoveAt and thought for a little bit. For some reason I thought if I make an if statement with an equivalent it would work, and it did.
static void Main(string[] args)
{
string[] array = new string[] { "Ultima Online", "Everquest", "Baldur's Gate", "Diablo I" };
List<string> list = new List<string>(array);
for (int i = 0; i < list.Count; i++)
{
if (i == 2)
{
list.RemoveAt(2);
}
Console.WriteLine(list[i]);
}
So, my questions are these:
Why do the exceptions take place at Remove() but not RemoveAt().
Why does this if statement work? I took a shot in the dark at it and it works, I can understand why it works, but then not why a simply RemoveAt() wouldn't work.
Last, is there a better way to convert than a linked list?
Thank you for anyone who answers these question. I am new to programming and I am trying to understand some concepts that flew past me in my book and on MSDN.com and DotPearl...
RemoveAt only removes the element at that index. It does not remove all the elements following it.
Unless.... you place it in a loop.
Which you did.
So your loop did this:
Remove element 2, output element 0
Remove element 2, output element 1
Remove element 2, output element 2
... and so on
This "works" because of this:
for (.....; i < list.Count; ..... )
^-- this ----^
Basically, your loop and list looked like this:
List is "Ultima Online", "Everquest", "Baldur's Gate", "Diablo I" (4 elements, 0-based, elements 0 through 3)
Index is 0, Remove element 2, "Baldur's Gate", Output element 0, "Ultima Online"
Index is 1, still below length (which is now 3), so remove element 2, "Diablo I", output element 1, "Everquest"
Index is 2, which is now equal to length (which is now 2), so exit
You changed the list while enumerating over it, and you changed it every iteration.
The reason why list.Remove(2) doesn't work is because of this:
list.RemoveAt(x) specifies an index, and the element at that index is removed
list.Remove(x) specifies a value, and the first element in the list that has that value will be removed
Now in this case you have said the list is a list of strings, and you ask it to remove an integer. C#/.NET doesn't allow such shenanigans, and thus told you about the problem before even allowing you to run the program.
For more information, check out the documentation of:
List<T>
List<T>.RemoveAt method
List<T>.Remove method
Remove takes a parameter of type T which corresponds to the object you want to remove. In your instance, Remove takes a string parameter which is supposed to correspond with one of the string items in your list (e.g. Ultima Online).
RemoveAt takes an int which corresponds to the index of the item in the list you want to remove. Once removed, everything is "shifted", so that what was at index 3 is now at index 2. When your loop passes over the RemoveAt(2) again, it removes the item that is now at index 2.
Why do the exceptions take place at Remove() but not RemoveAt()?
You get the exception Argument 1: cannot convert from 'int' to 'string' because the method signature is List<T>.Remove<T>(T). It expects a string.. and you're giving it a number.
Why does this if statement work?
Because you're completely free to compare numbers with numbers. Again, you've used RemoveAt here.. which expects a number.. and you've correctly provided one.
Also, a List<T> isn't a linked list. It is in fact a list backed by an array. An important distinction imo.
RemoveAt works by index. Remove is using the types definition of equality. You have a List<string>, the type is string, string does character by character comparison to determine equality, to remove a string you need to do;
list.Remove("Diablo I");
The issue with your RemoveAt getting rid of everything but the first item is well explained by Dave Zych's answer so I won't go into it.
I will talk about you converting to a list and your use of the term "Linked List" though. So firstly, List<T> in .NET is in no way a linked list. It behaved like one in some ways, however it is backed by an array. There is no need for you to convert to it. To print your array you could do either of the following;
for (int i = 0; i < array.Length; i++)
Console.WriteLine(array[i]);
or
Console.WriteLine(String.Join("\n", array));
You could easily "remove" an item by doing;
array[i] = null;
Assuming you haven't done stuff like string temp = array[i] elsewhere before that line you would be setting the final existing reference to that string to null which basically puts in queue to be GC'd.
Say I have
List<int> ages = new List<int>() { 8, 5, 3, 9, 2, 1, 7 };
List<int> marks = new List<int>() { 12, 17, 08, 15, 19, 02, 11 };
I can sort my marks by ages like this:
while (true)
{
bool swapped = false;
for (int i = 0; i < ages.Count - 1; i++)
if (ages[i] > ages[i + 1])
{
int tmp = ages[i];
ages[i] = ages[i + 1];
ages[i + 1] = tmp;
tmp = marks[i];
marks[i] = marks[i + 1];
marks[i + 1] = tmp;
swapped = true;
}
if (!swapped)
break;
}
Now I want to put this into a function that accepts any two lists. The first parameter will be the reference list, the numerical or comparable list. The second parameter will be the list containing the data.
For example:
public static void Sort<T>(List<T> RefList, List<T> DataList)
{
// sorting logic here...
}
There are a few problems:
First of all, T is almost certainly not the same type in RefList and DataList. RefList might be dates, integers, or doubles; whereas DataList is free to be absolutely anything. I need to be able to receive two, arbitrary generic types.
Secondly, I cannot seem to use the > operator with the T in this line:
if (ages[i] > ages[i + 1])
Perhaps my whole approach is wrong.
By the way, I have read responses to similar questions that suggest that the two lists should be combined into a single list of a compound data type. This isn't practical at all for my application. All I want to do is write a static function that somehow sorts one list based on the elements of another.
To sort one list the way you want you actually need to somehow keep references from items in first list to they weight/keys in the second list. No existing methods do that as you can't easily associate metadata with arbitrary values (i.e. if first list is list of int as in your case there is nothing to map to keys in second list). Your only reasonable option is to sort 2 lists at the same time and make association by index - again no existing classes to help.
It may be much easier to use solution that you reject. I.e. simply Zip and OrderBy, than recreate first list:
ages = ages
.Zip(marks, (a,m)=> new {age = a; mark = m;})
.OrderBy(v => v.mark)
.Select(v=>v.age)
.ToList();
Note (courtesy of phoog): if you need to do this type of sorting with Array there is Array.Sort that allows exactly this operatiion (see phoog's answer for details).
There's no framework method to do this with List<T>, but if you don't mind putting the data into two arrays, you can use one of the Array.Sort() overloads that takes two arrays as arguments. The first array is the keys, and the second is the values, so your code might look like this (leaving aside the step of getting arrays from the lists):
Array.Sort(ages, marks);
The specifics of getting the values into arrays and then back into lists would depend, among other things, on whether you need to end up with the same list sorted appropriately or whether it's okay to return a new list with the data in the desired order.
Use:
public static void Sort<TR, TD>(IList<TR> refList, IList<TD> dataList)
where TR : System.IComparable<TR>
where TD : System.IComparable<TD>
{
...
}
and then use:
refList[i].CompareTo(refList[i+1])
instead of the operators.
.Net numbers already implement IComparable, and you can use overloads that allow you to specify a different IComparable.
If I understand "I can sort my marks by ages like this:" properly,
I would like to suggest the below to eliminate much confusion.
struct Student{
int age;
int marks;
};
List<Student> students = {{8,12}, ...};
Now you can sort according to age and marks is accordingly sorted automatically.
If it is not possible, you need to fix the code as below.
First of all, T is almost certainly not the same type in RefList and DataList.
Then you need 2 parameters T1, T2. Just T implies the types are the same.
public static void Sort<RefType, DataType>(List<RefType> RefList, List<DataType> DataList)
{
You can also zip the two lists together as suggested by Mechanical Snail and explained in Looping through 2 Lists at once
I'll just go straight to the point. I want to move the items in an array in a uniform difference, let's say I have this.
string[] fruits = { "Banana", "Apple", "Watermelon", "Pear", "Mango" };
For example, let's say I want to remove the "Apple" so I'll do this.
fruits[1] = "";
Now all that left are:
{ "Banana", "", "Watermelon", "Pear", "Mango" }
How do I really remove the Apple part and get only:
{ "Banana", "Watermelon", "Pear", "Mango" }
Note that the index of all the items from "Watermelon" until the end of the array moves 1 backward. Any ideas?
The List class is the right one for you. It provides a method Remove which automatically moves the following elements backwards.
If you really want to use Arrays, you can use Linq to filter your list and convert to array:
string[] fruits = { "Banana", "Apple", "Watermelon", "Pear", "Mango" };
fruits = fruits.Where(f => f != "Apple").ToArray();
If you're not required to use an array, look at the List class. A list allows items to be added and removed.
Similar to Wouter's answer, if you want to remove by item index rather than item value, you could do:
fruits = fruits.Where((s, i) => i != 1).ToArray();
You can do something like this:
for( int i = 1; i + 1 < fruits.Length; i++ )
fruits[i] = fruits[i + 1];
fruits = System.Array.Resize( fruits, fruits.Length - 1 );
If you do not care about the order of the fruit in the array, a smarter way to do it is as follows:
fruits[1] = fruits[fruits.Length - 1];
fruits = System.Array.Resize( fruits, fruits.Length - 1 );
I think one of the most useful things a new programmer can do is study and understand the various collection types.
While I think the List option that others have mentioned is probably what you are looking for, it's worth looking at a LinkedList class if you are doing a lot of insertions and deletions and not a lot of looking up by index.
This is an example of how I used lists and arrays to remove an item from an array. Note I also show you how to use linq to search an array full of bad names to remove. Hope this helps someone.
public static void CheckBadNames(ref string[] parts)
{
string[] BadName = new string[] {"LIFE", "ESTATE" ,"(",")","-","*","AN","LIFETIME","INTREST","MARRIED",
"UNMARRIED","MARRIED/UNMARRIED","SINGLE","W/","/W","THE","ET",
"ALS","AS", "TENANT" };
List<string> list = new List<string>(BadName); //convert array to list
foreach(string part in list)
{
if (BadName.Any(s => part.ToUpper().Contains(s)))
{
list.Remove(part);
}
}
parts = list.ToArray(); // convert list back to array
}
As a beginner 3 years ago, I started making the software that I'm still working on today. I used an array for 'PartyMembers' of the game, and I'm basically today regretting it and having to spend a ton of time converting all this hard coded $#!t into a list.
Case in point, just use Lists if you can, arrays a nightmare in comparison.
I have string containing n elements.
I want to insert a automatically incremented number to the first position.
E.g
Data
66,45,34,23,39,83
64,46,332,73,39,33
54,76,32,23,96,42
I am spliting to string to array with split char ','
I want resultant array with a incremented number a first position
1,66,45,34,23,39,83
2,64,46,332,73,39,33
3,54,76,32,23,96,42
Please suggest how can I do it.
Thanks
You can't with an array, you need to use a List<string> instead.
For example:
List<string> words = new string[] { "Hello", "world" }.ToList();
words.Insert(0, "Well");
Linq has an easy way to accomplish your "mission":
First add the correct namespace:
using System.Linq;
And then:
// Translate your splitted string (array) in a list:
var myList = myString.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList<string>();
// Where insertPosition is the position you want to insert in:
myList.Insert(insertingPosition, insertedString);
// Back to array:
string[] myArray = myList.ToArray<string>();
Hope this helps...
You cannot insert anything to Array. Use List<T> instead.
You have to use something like an ArrayList instead, which has an ArrayList.Insert() method.
ArrayList myAL = new ArrayList();
myAL.Insert( 0, "The" );
myAL.Insert( 1, "fox" );
myAL.Insert( 2, "jumps" );
myAL.Insert( 3, "over" );
myAL.Insert( 4, "the" );
myAL.Insert( 5, "dog" );
well what if you have the list like the following
string a="66,45,34,23,39,83";
string b="64,46,332,73,39,33";
string c="54,76,32,23,96,42";
before splitting a,b,c...
string[] s=new string[]{a,b,c};
for(int i=0; i<s.length;i++){
s[i]=(i+1)+s[i];
}
now split each string in s
you will have a list like
1,66,45,34,23,39,83
2,64,46,332,73,39,33
3,54,76,32,23,96,42
I am not sure if I have understood your problem or not. :|
I know you want a C# answer (and there are good answers here already in that language), but I'm learning F# and took this on in that language as a learning exercise.
So, assuming you want an array of strings back and are willing to use F#...
let aStructuredStringArray = [|"66,45,34,23,39,83"
"64,46,332,73,39,33"
"54,76,32,23,96,42"|]
let insertRowCountAsString (structuredStringArray: string array) =
[| for i in [0 .. structuredStringArray.Length-1]
do yield String.concat "" [ i.ToString(); ","; structuredStringArray.[i]]
|]
printfn "insertRowCountAsString=%A" (insertRowCountAsString aStructuredStringArray)
C# arrays cannot be resized. This means that you can't insert items.
You have two options:
Use a List<T> instead.
Create a new array, with one extra item in, copy the contents of the old one across and so on.
Option 1 is invariably to be preferred.
There is Array.Resize(T). On the face of it this contradicts what I state above. However, the documentation for this method states:
This method allocates a new array with the specified size, copies elements from the old array to the new one, and then replaces the old array with the new one.
I have 2 separate List and I need to compare the two and get everything but the intersection of the two lists. How can I do this (C#)?
If you mean the set of everything but the intersection (symmetric difference) you can try:
var set = new HashSet<Type>(list1);
set.SymmetricExceptWith(list2);
You can use Except to get everything but the intersection of the two lists.
var differences = listA.Except(listB).Union(listB.Except(listA));
If you want to get everything but the union:
var allButUnion = new List<MyClass>();
(The union is everything in both lists - everything but the union is the empty set...)
Do you mean everything that's only in one list or the other? How about:
var allButIntersection = a.Union(b).Except(a.Intersect(b));
That's likely to be somewhat inefficient, but it fairly simply indicates what you mean (assuming I've interpreted you correctly, of course).
Here is a generic Extension method. Rosetta Code uses Concat, and Djeefther Souza says it's more efficient.
public static class LINQSetExtensions
{
// Made aware of the name for this from Swift
// https://stackoverflow.com/questions/1683147/get-the-symmetric-difference-from-generic-lists
// Generic implementation adapted from https://www.rosettacode.org/wiki/Symmetric_difference
public static IEnumerable<T> SymmetricDifference<T>(this IEnumerable<T> first, IEnumerable<T> second)
{
// I've used Union in the past, but I suppose Concat works.
// No idea if they perform differently.
return first.Except(second).Concat(second.Except(first));
}
}
I haven't actually benchmarked it. I think it would depend on how Union vs. Concat are implemented. In my dreamworld, .NET uses a different algorithm depending on data type or set size, though for IEnumerable it can't determine set size in advance.
Also, you can pretty much ignore my answer - Jon Skeet says that the HashSet method "Excellent - that looks like the best way of doing it to me."
Something like this?
String[] one = new String[] { "Merry", "Metal", "Median", "Medium", "Malfunction", "Mean", "Measure", "Melt", "Merit", "Metaphysical", "Mental", "Menial", "Mend", "Find" };
String[] two = new String[] { "Merry", "Metal", "Find", "Puncture", "Revise", "Clamp", "Menial" };
List<String> tmp = one.Except(two).ToList();
tmp.AddRange(two.Except(one));
String[] result = tmp.ToArray();
var theUnion = list1.Concat(list2);
var theIntersection = list1.Intersect(list2);
var theSymmetricDifference = theUnion.Except(theIntersection);
Use Except:
List<int> l1 = new List<int>(new[] { 1, 2, 3, 4 });
List<int> l2 = new List<int>(new[] { 2, 4 });
var l3 = l1.Except(l2);