I'm fairly new to C# so this might be difficult for me to put into words.
I am creating a media application and I have a class (called MediaFile) that contains information about media files (name, size, play count and tags). These are of string, double, int and List<string> types, respectively.
I have a list of these objects, so for example to call MediaFile[2].Tags will refer to a list of "tag" strings (related keywords).
The problem I have is that when I ask the user to enter tags for the selected MediaFile, whenever I try to save those tags to that specific object, the tags get saved to every single object. The code for the assignment currently looks something like this:
MediaFile[lstLibrary.SelectedIndices[0]].Tags.Add(tempTag);
'tempTag' is a string that I'm trying to add into the list of strings, but like I said - even though only one file is selected, the 'tempTag' string gets added into the list of strings of each MediaFile.
Can anyone shed some light on what I'm doing wrong?
Many thanks.
EDIT: Thanks for all of your responses. Whenever I create a MediaFile instance I pass new List<string> to the constructor. Then later on when I go to change this list of strings I discover all of the MediaFiles seem to have the same string reference. Here is the MediaFile class:
public static List<MediaType> MediaFile = new List<MediaType>();
public class MediaType
{
public string Name;
public string Path;
public double Size;
public int Plays;
public List<string> Tags;
// Constructor for adding new files
public MediaType(string path, List<string> tags)
{
Path = path;
Tags = tags;
}
And after I ask the user to select a file to add to the media library:
MediaFile.Add(new MediaType(Path.GetFullPath(file), new List<string>()));
So there are no 'tags' at first, but then later on (and herein lies my problem):
if (tempTag != "")
MediaFile[lstLibrary.SelectedIndices[0]].Tags.Add(tempTag);
}
Any idea? Apologies this post is so long!
That suggests you've added the same string reference to all the MediaFiles. In other words, you might have done:
List<string> tags = new List<string>();
for (int i = 0; i < 100; i++)
{
MediaFile file = new MediaFile(tags);
MediaFiles.Add(file);
}
This is very easy to test - just check whether MediaFile[0].Tags == MediaFile[1].Tags - I suspect you'll find that evaluates to True, meaning you're using the same list for both files.
Instead, you should create a new List<string> for each MediaFile instance.
If you could post some code, that would help us to pin down the problem more precisely, of course...
It looks like you have set all of the MediaFiles[i].Tags to be references to the same instance of a list.
Where do you put stuff in MediaFile? How do you do it?
My guess is that you're inserting the same MediaFile object several time in the list. So, when you modify one, you end up modifying all of them because, after all, they are the same object.
It sounds like you've set all the objects in your list to use the same instance of the Tags list
I suspect that you're initialising your objects incorrectly. You're probably (effectively) creating them like this:
List<string> tags = new List<string>();
MediaFile m1 = new MediaFile();
m1.Tags = tags;
MediaFile m2 = new MediaFile();
m2.Tags = tags;
That is, the Tags property of each MediaFile refers to the same actual List<string> object. Thus, when you add a string to m1.Tags, you're also adding it to m2.Tags.
What you want to do instead is:
MediaFile m1 = new MediaFile();
m1.Tags = new List<string>();
MediaFile m2 = new MediaFile();
m2.Tags = new List<string>();
Does your Tags property have a set accessor?
Or do you have code that looks (something) like this?
var tags = new List<string>();
for (int i = 0; i < MediaFile.Count; ++i)
{
MediaFile[0] = new MediaFile(tags);
}
If so then each of your MediaFile instances is sharing the same internal List<string>.
You should not have a set accessor on this property at all, actually, nor should the MediaFile constructor be willing to take some external List<string> (unless it's going to copy it). Instead assign it only in the constructor for MediaFile to a new List<string>(); this way you will ensure that each instance gets its own list:
public MediaFile()
{
this.Tags = new List<string>();
}
Or:
public MediaFile(IEnumerable<string> tags)
{
this.Tags = new List<string>(tags);
}
Related
I'm not that great with coding and all since we've started learning the basics this year, and my groupmates aren't doing anything to help me, so I'm lost here and wanted to seek help from all the experts that are here.
I was wondering how I'd be able to sort the mixed data inside the Textbox properly
(I'm using the console.application to access windows.Forms because that's what our professor wants us to do) . As you can see in the image below, it gets arranged alphabetically instead of sorting the score in descending order with the names of the player beside their scores.
void show_Click(object sender, EventArgs e)
{
int counter = 0;
string line;
StreamReader TXT = new StreamReader("Leaderboard.txt");
List<string> list = new List<string>();
while ((line = TXT.ReadLine()) != null)
{
ListB1.Items.Add(line);
list.Add(line);
counter++;
}
string[] arr = list.ToArray();
Array.Sort(arr);
Array.Reverse(arr);
foreach (string item in arr)
{
ListB2.Items.Add(item);
}
this is barely a fraction of my whole code but this is the part I think is most essential to making my leaderboards function properly.
this is what my code results to, as of now...
Currently you're reading each line of the file as a single string. So there's no difference between "score" and "text", it's all just one text value.
It sounds like what you want is to parse that information into an object with two different properties. For example, suppose you have this class:
public class ScoreListing
{
public int Score { get; set; }
public string Text { get; set; }
}
Then your list would be something like:
List<ScoreListing> list = new List<ScoreListing>();
And to add to it, you'd need to parse information from line to create a new ScoreListing object. For example, it might look something like this:
while ((line = TXT.ReadLine()) != null)
{
var elements = line.Split(' | ');
var listing = new ScoreListing
{
Score = int.Parse(elements[0]),
Text = elements[1]
};
list.Add(listing);
counter++;
}
Once you have a populated List<ScoreListing> you can sort it with .OrderBy(), for example:
var sortedLeaderboard = list.OrderByDescending(l => l.Score);
There are multiple ways to approach the storage of the data, sorting the data, etc. But overall when you have a value like "123 | SomeText" then what you really have is a custom-serialized object with two distinct properties. Your first goal when reading this data into memory should be to de-serialize it into that meaningful object. Then you can easily use that object however you like.
By request, a bit more information on converting data back into a string...
The most object-oriented approach would be to override .ToString() on the class:
public class ScoreListing
{
public int Score { get; set; }
public string Text { get; set; }
public override string ToString()
{
return $"{Score} | {Text}";
}
}
Then you can add it to your list of strings:
foreach (var item in sortedLeaderboard)
{
ListB2.Items.Add(item.ToString());
}
Without overriding .ToString() you'd just manually perform the same operation:
foreach (var item in sortedLeaderboard)
{
ListB2.Items.Add($"{item.Score} | {item.Text}");
}
How do i get items from array element json c# ?
I want to get multiple artist names from my call, and put them in a listbox.
I can get the first artist like this etc.:
string url = #"http://api.musixmatch.com/ws/1.1/artist.search?apikey=key&q_artist=LMFAO&format=json";
string content = new WebClient().DownloadString(url);
dynamic artistData = JObject.Parse(content);
string artistName = artistData.message.body.artist_list[0].artist.artist_name;
But how can i get more of the names and put them in a listbox?
Here you can see how the json result looks like
Thanks in advance
You could try something like this
var artists = artistData.message.body.artist_list.Select(x => x.artist);
foreach (var artist in artists)
{
var artistName = artist.artist_name;
listBox.Items.Add(new ListBoxItem(artistName , artistName));
}
Hi you can use the below code snippet to get list of names
var artistNames = artistData.message.body.artist_list.Select(var=>var.artist.artist_name).ToList();
Then you could bind this list to your listbox either by using listbox.items.add function calls or by making use of ItemsSource property
Thanks for trying to help me, I really appreciate it. I figured out i could solve my problem like this:
var artistLength = artistData.message.body.artist_list.Count;
for (int i = 0; i < artistLength; i++)
{
var artistName = artistData.message.body.artist_list[i].artist.artist_name;
//printed in the console for testing
Console.WriteLine(artistName);
listBox1.Items.Add(artistName);
}
I am fairly new to C#
I am trying to retrieve some information from an external data source and store it in array, once it is in an array I wish to sort it by time.
I know how to do this for just one column in a row, however the information I require has multiple columns.
For example:
foreach (Appointment Appoint in fapts)
{
// Store Appoint.Subject, Appoint.Start, Appoint.Organiser.Name.ToString(), Appoint.Location in an array
}
// Sort my array by Appoint.Start
foreach ( item in myNewArray )
{
//print out Appoint.Subject - Appoint.Start, Appoint.Organiser.Name.ToString() and Appoint.location
}
Many thanks for your help.
EDIT:
I have multiple data sources which pull in this:
foreach (Appointment Appoint in fapts)
{
// Store Appoint.Subject, Appoint.Start, Appoint.Organiser.Name.ToString(), Appoint.Location in an array
}
Hence the need to sort the items in a new array, I know this isn't very efficent but there is no way of getting the information I need in any other way.
You can sort a list using the LINQ sorting operators OrderBy and ThenBy, as shown below.
using System.Linq;
and then...
var appointments = new List<Appointment>();
var sortedAppointments = list.OrderBy(l => l.Subject).ThenBy(l => l.Name).ToList();
This will create a new list of appointments, sorted by subject and then by name.
It's unclear what your final aim is but:
Use a generic List instead of an array:
See this SO question for more information as to why using a List is prefered.
List<Appointment> appointments = new List<Appointment>();
foreach (Appointment Appoint in fapts)
{
appointments.Add(Appoint);
}
foreach (var item in appointments)
{
Console.WriteLine(item.Subject);
Console.WriteLine(item.Foo);
// Here you could override ToString() on Appointment to print eveything in one Console.WriteLine
}
If the aim of your code is to order by time, try the following:
var sortedAppointments = fapts.OrderBy(a => a.Start); // assuming Start is a DateTime property of `Appointment`.
Consider a Dictionary Object instead of an array if the data is conceptually one row multiple columns.
foreach(KeyValuePair<string, string> entry in MyDic)
{
// do something with entry.Value or entry.Key
}
You already have a list of objects in fpts, sort that list itself:
fpts.OrderBy(x => x.Subject).ThenBy(x => x.Location).ToList();
LINQ is your friend here.
fapts appears to already be a collection so you could just operate on it.
var myNewArray = fapts.OrderBy(Appoint => Appoint.Start).ToArray()
I've used the ToArray() call to force immediate evaluation and means that myNewArray is already sorted so that if you use it more than once you don't have to re-evaluate the sort.
Alternatively if you are only using this once you can just as easily miss the ToArray() portion out and then execution of the sort will be deferred until you try and enumerate through myNewArray.
This solution puts the source objects into the array, but if you are just wanting to store the specific fields you mention then you will need to use a select. You have two choices for the array item type, you can either use an anonymous class which provides difficulties if you are returning this array from a function or define a class.
For anonymous:
var myNewArray = fapts.OrderBy(Appoint => Appoint.Start)
.Select(Appoint => new {
Start = Appoint.Start,
Organiser = Appoint.Organiser.Name.ToString(),
Location = Appoint.Location
}).ToArray();
For named class assuming class is MyClass:
var myNewArray = fapts.OrderBy(Appoint => Appoint.Start)
.Select(Appoint => new MyClass {
Start = Appoint.Start,
Organiser = Appoint.Organiser.Name.ToString(),
Location = Appoint.Location
}).ToArray();
You have a wide range of options. The 2 most common are:
1) Create a class, then define an array or list of that class, and populate that
2) Create a structure that matches the data format and create an array or list of that
Of course, you could put the data into an XML format or dataset, but that's probably more work than you need.
public List<foo> appointments = new List<foo>();
public struct foo
{
public string subject ;
public DateTime start ;
public string name ;
public string location ;
}
public void foo1()
{
// parse the file
while (!File.eof())
{
// Read the next line...
var myRecord = new foo() ;
myRecord.subject = data.subject ;
myRecord.start = data.Start ;
myRecord.name = data.Name ;
//...
appointments.Add(myRecord);
}
}
Enjoy
(Since I can't comment and reply to the comment - it wasn't clear if he had a class, etc. or was just showing us what he wanted to do. I assumed it was just for demonstration purposes since there wasn't any info as to how the data was being read. If he could already put it into a class, than the first answer applied anyway. I just tossed the last 2 in there because they were options for getting the data first.)
I have a foreach statement that outputs a list of customer id's to a log file:
foreach(var customer in _response.CustomersList)
{
log.WriteLine(customer.CustID);
}
The Id's output correctly but the problem I have is I am not able to put them into one variable use them.
For example I wanted to give this request: _request2.CustID = customer.CustID but this is not correct.
I need those ID's because I have a cancel customer request:
public void Cancel()
{
_request2 = new CancelCust();
_request2.CommandUser = _request.CommandUser;
_request2.CustID = "This is where I would put the variable that holds the customer ID's"
_request2.Company = _request.Company;
}
So, how do I assign those id's to a variable to be used in my request later?
I'm not totally clear on what you are trying to accomplish with the code above. BUT, you could use a bit of LINQ and get a list of the IDs, then pass around that list as you like:
var customerIDs = _response.CustomersList.Select(customer => customer.CustID);
You can get all the customer Ids by storing them as you go:
var customerIds = new List<int>();
foreach (var customer in _response.CustomersList)
{
customerIds.Add(customer.CustId);
log.WriteLine(customer.CustID);
}
or by using LINQ
var customerIds = _response.CustomersList.Select(c => c.CustId);
As I pointed out in my comment, if the property Request.CustId is not a collection this won't help you request all of them at once. Are you able to change the definition of Request?
It sounds like your variable _request2.CustID is the wrong type. What error message are you getting from the compiler? (Or, is it a run-time error that you get?)
Something like this?
public void Cancel()
{
foreach(var customer in _response.CustomersList)
{
_request2 = new CancelCust();
_request2.CommandUser = _request.CommandUser;
_request2.CustID = customer.CustID;
_request2.Company = _request.Company;
}
}
I'm trying to build a dictionary of lists, but i'm reading in a string and need to make the lists name the string and add them to the dictionary as a key.
IE read in "hello"
Create the list with what was read in
List<string> (insert read string here ) = new List<string>();
Then add that lists name as the key to a dictionary.
Dictionary.Add(readstring, thatlist);
All I can find is a hard code implementation of this.
Turing.Add("S", S);
My goal: create a Universal Turing Machine, so I read in an input from a text file that is the next step that looks like this, (q0 a) -> q1 X R.
Then use the all the steps that I read in to end in a final state with this on the virtual tape "tape = XXYYZZBB"
I have the the pseudo code written for this but I just cant get the dictionary to work.
EDIT:
Adding some more info for less confusion.
Im given the start and the end state for the first 2 lines of the text file. Then im given the transitions.
q0 //start state
q5 //end state
q0 a q1 X R //transitions
Ive stripped the first two lines of input to give me 0 and 5 then have created a for loop to create a lists of each state.
for (int i = 0; i <= endState; i++)
{
List<string> i = new List<string>();
}
I then want to add each lists name as a key to the dictionary of lists I am creating.
Dictionary.Add(listname, thatlist);
I need help on implementing the code for the above as its giving errors.
It doesn't matter whether you create your list as
List<string> insertReadStringHere = new List<string>();
or
List<string> foo = new List<string>();
or even
List<string> shellBeComingRoundTheMountain = new List<string>();
What's important is that once you've done
MyDictionary.Add(theInputString, shellBeComingRoundTheMountain);
you can then access that particular list via
MyDictionary[theInputString]
wherether the initial list was "called" insertReadStringHere or foo or shellBeComingRoundTheMountain.
You don't even need to hold the list in a named variable like this. For example,
Console.WriteLine("Input a string to create a list:");
var createListName = Console.ReadLine();
// As long as they haven't used the string before...
MyDictionary.Add(createListName, new List<string>());
Console.WriteLine("Input a string to retrieve a list:");
var retrieveListName = Console.ReadLine();
// As long as they input the same string...
List<string> retrievedList = MyDictionary[retrieveListName];
Edit: If you want a certain number of lists, use a dictionarym apping from int to string, not string to string:
int maxNumberOfLists = 5; // or whatever you read from your text file.
Dictionary<int, List<string>> myLists =
new Dictionary<int, List<string>> (maxNumberOfLists);
for (int i = 1; i <= maxNumberOfLists; i++)
myLists[i] = new List<string>();
Then you can access your lists as e.g.
var firstList = myLists[1];
Ordinarily I'd recommend an array, but this gives you lists from 1 to 5 rather than from 0 to 4 and it seems that's what you want.