I have got 4 questions:
How is it possible that I am able to print Console.WriteLine(list.head.Next.Next.Data).
From the AddHead(), the new object will save over the previous object n.Next?
I created a custom class to try and understand if I the above example is only List specific.
So, how is it possible that I can Console.WriteLine(cc.head.Next.Next.Next.Data) ?
How do I post code inside a box for the forum, this is my first post, please forgive me.
Thank you !
using System;
namespace GenericInterfaces
{
class Program
{
static void Main()
{
//Declare and instantiate a new generic SortedList class.
//Person is the type argument.
SortedList<Person> list = new SortedList<Person>();
//Create name and age values to initialize Person objects.
string[] names = new string[]
{
"Franscoise",
"Bill",
"Li",
"Sandra",
"Gunnar",
"Alok",
"Hiroyuki",
"Maria",
"Alessandro",
"Raul"
};
int[] ages = new int[] { 45, 19, 28, 23, 18, 9, 108, 72, 30, 35 };
//Populate the list with new Node Objects
for (int x = 0; x < 10; x++)
{
//ages[x] is integer
list.AddHead(new Person(names[x],ages[x]));
}
//Print out unsorted list.
foreach (var p in list)
{
System.Console.Write(p.ToString() + " ");
Console.WriteLine("Unsorted Data");
Console.WriteLine(list.head.Data);
Console.WriteLine(list.head.Next.Data);
Console.WriteLine(list.head.Next.Next.Data);
Console.WriteLine(list.head.Next.Next.Next.Data);
}
System.Console.WriteLine("Done with unsorted list");
Console.WriteLine("My Custom Class Experiment");
MyCustomClass cc = new MyCustomClass();
for (int i = 0; i < ages.Length; i++)
{
cc.AddHead(ages[i]);
}
Console.WriteLine("Added ages to custom class");
Console.WriteLine(cc.head.Data);
Console.WriteLine(cc.head.Next.Data);
Console.WriteLine(cc.head.Next.Next.Data);
Console.WriteLine(cc.head.Next.Next.Next.Data);
Console.WriteLine(cc.head.Next.Next.Next.Next.Data);
Console.WriteLine(cc.n.Data);
Console.WriteLine(cc.n.Next.Data);
}
}
//Type parameter T in angle brackets.
public class GenericList<T> : System.Collections.Generic.IEnumerable<T>
{
public Node head;
public Node current = null;
// Nested class is also generic on T
public class Node
{
public Node Next { get; set; } = null;
public T Data { get; set; }
public Node(T t) //T used in non-generic constructor
{
Data = t;
}
}
public GenericList() //constructor
{
head = null;
}
public void AddHead(T t) //T as method parameter type
{
Node n = new Node(t);
n.Next = head;
head = n;
}
// Implementation of the iterator
public System.Collections.Generic.IEnumerator<T> GetEnumerator()
{
Node current = head;
while (current != null)
{
yield return current.Data;
current = current.Next;
}
}
// IEnumerable<T> inherits from IEnumerable, therefore this class
// must implement both the generic and non-generic versions of
// GetEnumerator. In most cases, the non-generic method can
// simply call the generic method.
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
public class MyCustomClass
{
public Mode head;
public Mode current = null;
public Mode n;
// Nested class is also generic on T
public class Mode
{
//public Mode Next { get; set; } = null;
public Mode Next;
public int Data { get; set; }
public Mode(int t) //T used in non-generic constructor
{
Data = t;
}
}
public void AddHead(int t) //T as method parameter type
{
n = new Mode(t);
n.Next = head;
head = n;
}
}
the new object will save over the previous object n.Next?
In a LinkedList, the Next property of a list node doesn’t save or manipulate anything about the list, it just returns the Next item that the node is pointing to. The item returned is also a Node, that has its own Next, that points to yet another item in the list. If you repeatedly call Next then you simply travel along the list
Think of it as trying to find your child. You know he went to john’s house. You go to john’s house but are told “they went to paul’s house”, so you go to paul’s house and are told “they went to Sarah’s house”, so you go to Sarah’s house...
At each step you’re being sent to the Next house, but you’re not demolishing anyone’s house. You maintain a memory of where you are in the list of houses. If you went back to johns house and started over (the list head) you would travel the same route again
If you were to make a new variable pointing to the head of the list, and then repeatedly reassign it to Next, then you’re always changing the Node your temp variable is pointing to, but the list nodes themselves always stay pointing to each other too, and you don’t disturb their relationship:
var tempNode = list.Node; //tempNode is you, you’re at john’s house now
tempNode = tempNode.Next; //now you’re at paul’s house
tempNode = tempNode.Next; //now you’re at sarah’s
At no time did you disturb any of the existing nodes. What if, when you got to Paul’s house you said to Paul’s dad “if my wife comes asking, tell her my son went to Jane’s house” - this changes where Paul’s dad will point the next person to:
var tempNode = list.Node; //tempNode is you, you’re at john’s house now
tempNode = tempNode.Next; //now you’re at paul’s house
tempNode.Next = new Node(“Jane’s house”);
Now we have actually manipulated the list and changed where the Next of the second node is pointing to
how does AddHead work?
It takes the data you pass in and makes a new Node out of it, then makes the new node's Next point to the existing list head and makes the existing list head point to the new node instead. This effectively makes the list grow backwards by adding a new head all the time. If you wanted the houses to be visited in order of John, Paul, Sarah you'd have to add them in order of AddHead(Sarah), AddHead(Paul), AddHead(John)
The list growth looks like:
head->nothing
head->Sarah
head->Paul->Sarah
head->John->Paul->Sarah
I simplified the look of this, to hide Nodes and Next. Every arrow -> represents Next, every name is the Data inside a Node
how do I post code
Put 3 back ticks on their own line, before and after the code block. Or prefix every line in the code with 4 or more spaces
how should I name the question
That’s really for you to decide, as it helps us understand how you think as does the question body
Related
apologies if I'm doing something wrong, this is my first post.
I'm currently working with C# and want to save a bunch of data out to a JSON file and load it back, but I'm having trouble figuring out how to get it in the following format.
// Primary ID
001
{
// Secondary ID
01
{
// Tertiary ID
01
{
string: "this is some information.",
int: 9371
}
}
// Secondary ID
02
{
// Tertiary ID
01
{
string: "blah blah blah.",
int: 2241
}
}
}
I'd essentially like to be able to call up information with a particular set of IDs for example 001-02-01 which would return a string ("blah blah blah.") and an int (2241).
The reason I want to go about it like this instead of just having one longer ID is so that when the JSON file becomes very large, I'm hoping to be able to speed up the search for information by passing each ID in turn.
If that makes no sense and it would be equally as fast to just pass in one longer ID and not be bothered by this whole nested ID segments concept then please let me know!
If, however what I'm thinking is correct and it would help the speed of finding particular data by structuring it out like this, how would I go about doing that? With nested C# classes in arrays?
The most simple way and efficient way would be to have all data as same type. Currently, you seem to go for each object is of type of the given id:
{
"01":{},
"02" :{}
}
this will not go too well if trying to use a serializable class.
I would recommend the following:
{
"items" : [
{"id":"01" }, { "id":"02" },...
]
}
Then you can serialize/deserialize easily with
[Serializable]
public class Item
{
public string id = null;
}
[Serializable]
public class RootObject
{
public List<Item> items = null;
}
and then in Unity:
void Start(){
string str = GetJson(); // However you get it
RootObject ro = JsonUtility.FromJson<RootObject>(str);
}
if you want to speed up the fetching and your collection is large, convert to dictionary.
Dictionary<string, Item> dict = null;
void Start(){
string str = GetJson(); // However you get it
RootObject ro = JsonUtility.FromJson<RootObject>(str);
this.dict = new Dictionary<string,Item>();
foreach(Item item in ro.items){
Item temp = temp;
this.dict.Add(item.Id, temp);
}
ro = null;
}
Now you can access real fast.
Item GetItem(string id)
{
if(string.IsNullOrEmpty(id) == true){ return null; }
Item item = null;
this.dict.TryGetValue(id, out item);
return item;
}
If you end up storing millions of records in your file and want to start doing something more performant it would be easier to switch to a decent document database like MongoDB rather than trying to reinvent the wheel.
Worry about writing good standard code before worrying about performance problems that don't yet exist.
The following example is not in your language of choice but it does explain that JSON and arrays of 1,000,000 objects can be searched very quickly:
const getIncidentId = () => {
let id = Math.random().toString(36).substr(2, 6).toUpperCase().replace("O", "0")
return `${id.slice(0, 3)}-${id.slice(3)}`
}
console.log("Building array of 1,000,000 objects")
const littleData = Array.from({ length: 1000000 }, (v, k) => k + 1).map(x => ({ cells: { Number: x, Id: getIncidentId() } }))
console.log("Getting list of random Ids for array members [49, 60, 70000, 700000, 999999]")
const randomIds = ([49, 60, 70000, 700000, 999999]).map(i => littleData[i].cells.Id)
console.log(randomIds)
console.log("Finding each array item that contains a nested Id property in the randomIds list.")
const foundItems = littleData.filter(i => randomIds.includes(i.cells.Id))
console.log(foundItems)
I have four lists for four columns of a table. I want to print data as shown in the screenshot of the image below.
However, whenever I run the code below details of first user come according to the screenshot but after I input another user's data and try to print it, it comes in same line like this:
Jack Li Tom smith 10 5
This is what I have tried:
public void SummaryTable()
{
Console.WriteLine("\tSummary of Membership Fee");
Console.WriteLine("{0} {1,10} {2,10} {3,10}","Name", "Weeks","Discount","Charges");
//Console.Write("Weeks\t");
//Console.Write("Discount\t");
// Console.WriteLine("Charge");
//Console.WriteLine();
foreach (string namesl in nameList)
{
Console.Write("{0}",namesl);
//Console.Write("\t");
}
foreach (int weekl in weekList)
{
Console.Write("{0,10}",weekl);
//Console.Write("\t");
}
foreach (string discountl in discountList)
{
Console.Write("{0,10}", discountl);
// Console.Write("\t");
}
foreach (double chargel in chargeList)
{
Console.WriteLine("{0,10}",chargel);
}
You could just print them like this:
Console.WriteLine("\tSummary of Membership Fee");
Console.WriteLine(new String('=', 45));
Console.WriteLine("{0,-20} {1,-5} {2,-10} {3,-10}", "Name", "Weeks", "Discount", "Charges");
Console.WriteLine(new String('-', 45));
for (int count = 0; count < nameList.Count; count++)
{
Console.WriteLine("{0,-20}{1,5}{2,10}{3,10}", nameList[count], weekList[count], discountList[count], chargeList[count]);
Console.WriteLine(new String('-', 45));
}
These are parallel lists
I believe what you have here are called parallel lists. The first item in the first list goes with the first item in the second, third, and fourth lists; the second item in the first list goes with the second item in the other lists; etc. Is that correct?
What is needed here is to merge the lists into one, which you can do several ways. Here is a simple and intuitive way to do it. I'll show two:
Crayon method
This method is very simple and intuitive, although not all that fancy.
First, define a class that can contain the data in a single object:
public class Member
{
public string Name { get; set; }
public int Week { get; set; }
public string Discount { get; set; }
public double Charge { get; set; }
}
Then write a bit of code to merge your four lists into a single list of Member objects:
public List<Member> GetMergedList()
{
var results = new List<Member>();
for (int i=0; i<nameList.Count; i++)
{
results.Add(
new Member
{
Name = nameList[i],
Week = weekList[i],
Discount = discountList[i],
Charge = chargeList[i]
}
);
}
return results;
}
Now you can just iterate over (or DataBind against) the single list:
public void Run()
{
var members = GetMergedList();
foreach (var m in members)
{
Console.WriteLine("{0}\t{1}\r{2}\t{3:0.00}", m.Name, m.Week, m.Discount, m.Charge);
}
}
Output:
John 1 Discount1 1.23
Paul 2 Discount2 4.56
Dave 3 Discount3 7.89
Using LINQ
You could also merge your lists using LINQ (e.g. using the Zip method) to produce an anonymous type that contains everything:
var membersViaLinq = nameList
.Zip(weekList, (n, w) => new { Name = n, Week = w })
.Zip(discountList, (x, d) => new { Name = x.Name, Week = x.Week, Discount = d})
.Zip(chargeList, (x, c) => new { Name = x.Name, Week = x.Week, Discount = x.Discount, Charge = c });
foreach (var x in membersViaLinq)
{
Console.WriteLine("{0}\t{1}\r{2}\t{3:0.00}", x.Name, x.Week, x.Discount, x.Charge);
}
This approach is a little more confusing to read, but the benefit is you don't have to define a class just to hold the data.
The best solution
The best solution is to retrieve the data in a single list to begin with, if possible, and completely eliminate the parallel lists entirely. To suggest a way to do that, I'd have to see your data retrieval code.
Full example code on DotNetFiddle
This is easily solvable using OOP.
Create a following class:
public class Person
{
public string firstName;
public string lastName;
public int weeks;
public bool discount;
public int charge;
}
Now instead of creating multiple lists you will create only one list:
List<Person> People = new List<Person>();
You can populate it with Person objects.
Example:
People.Add(person1);
People.Add(person2);
People.Add(person3);
People.Add(person4);
Here I'm assuming you are going pass in a list of persons as an argument to this function:
public void SummaryTable(List<Person> list)
{
foreach (Person p in list)
{
Console.Write("{0}", p.firstName);
Console.Write("{0}", p.lastName);
Console.Write("{0}", p.weeks);
Console.Write("{0}", p.discount);
Console.Write("{0}", p.charge);
}
}
Now you simply call the function:
SummaryTable(People);
Also make sure you import the required namespaces using System.Collections;.
I'm creating a game in Unity3D + C#.
What I've got at the moment: an SQL datatable, consisting of 8 columns holding a total of 3 entries and a list "_WeapList" that holds every entry (as shown below).
public struct data
{
public string Name;
public int ID, dmg, range, magazin, startammo;
public float tbtwb, rltimer;
}
List<data> _WeapList;
public Dictionary<int, data>_WeapoList; //probable change
[...]
//reading the SQL Table + parse it into a new List-entry
while (rdr.Read())
{
data itm = new data();
itm.Name = rdr["Name"].ToString();
itm.ID = int.Parse (rdr["ID"].ToString());
itm.dmg = int.Parse (rdr["dmg"].ToString());
itm.range = int.Parse (rdr["range"].ToString());
itm.magazin = int.Parse (rdr["magazin"].ToString());
itm.startammo = int.Parse (rdr["startammo"].ToString());
itm.tbtwb = float.Parse(rdr["tbtwb"].ToString());
itm.rltimer = float.Parse(rdr["rltimer"].ToString());
_WeapList.Add(itm);
_WeapoList.Add(itm.ID, itm);//probable change
}
Now I want to create a "Weapon"-Class that will have the same 8 fields, feeding them via a given ID
How do I extract the values of a specific item (determined by the int ID, which is always unique) in the list/struct?
public class Weapons : MonoBehaviour
{
public string _Name;
public int _ID, _dmg, _range, _magazin, _startammo;
public float _tbtwb, _rltimer;
void Start()
{//Heres the main problem
_Name = _WeapoList...?
_dmg = _WeapoList...?
}
}
If your collection of weapons may become quite large or you need to frequently look up weapons in it, I would suggest using a Dictionary instead of a List for this (using the weapon ID as the key). A lookup will be much quicker using a Dictionary key than searching through a List using a loop or LINQ.
You can do this by modifying your code to do this as follows:
public Dictionary<int, data>_WeapList;
[...]
//reading the SQL Table + parse it into a new List-entry
while (rdr.Read())
{
data itm = new data();
itm.Name = rdr["Name"].ToString();
itm.ID = int.Parse (rdr["ID"].ToString());
itm.dmg = int.Parse (rdr["dmg"].ToString());
itm.range = int.Parse (rdr["range"].ToString());
itm.magazin = int.Parse (rdr["magazin"].ToString());
itm.startammo = int.Parse (rdr["startammo"].ToString());
itm.tbtwb = float.Parse(rdr["tbtwb"].ToString());
itm.rltimer = float.Parse(rdr["rltimer"].ToString());
_WeapList.Add(itm.ID, itm);//probable change
}
Then, to access elements on the list, just use the syntax:
_WeapList[weaponID].dmg; // To access the damage of the weapon with the given weaponID
Guarding against invalid IDs:
If there's a risk of the weaponID supplied not existing, you can use the .ContainsKey() method to check for it first before trying to access its members:
if (_WeapList.ContainsKey(weaponID))
{
// Retrieve the weapon and access its members
}
else
{
// Weapon doesn't exist, default behaviour
}
Alternatively, if you're comfortable using out arguments, you can use .TryGetValue() instead for validation - this is even quicker than calling .ContainsKey() separately:
data weaponData;
if (_WeapList.TryGetValue(weaponID, out weaponData))
{
// weaponData is now populated with the weapon and you can access members on it
}
else
{
// Weapon doesn't exist, default behaviour
}
Hope this helps! Let me know if you have any questions.
Let specificWeapon be a weapon to be searched in the list, then you can use the following code to select that item from the list of weapons, if it is not found then nullwill be returned. Hope that this what you are looking for:
var selectedWeapon = WeapList.FirstOrDefault(x=> x.ID == specificWeapon.ID);
if(selectedWeapon != null)
{
// this is your weapon proceed
}
else
{
// not found your weapon
}
You can use LINQ to search specific object through weaponId
var Weapon = _WeapList.FirstOrDefault(w=> w.ID == weaponId);
public class TestObject
{
string TestValue { get; set; }
bool IsDuplicate { get; set; }
}
List<TestObject> testList = new List<TestObject>
{
new TestObject { TestValue = "Matt" },
new TestObject { TestValue = "Bob" },
new TestObject { TestValue = "Alice" },
new TestObject { TestValue = "Matt" },
new TestObject { TestValue = "Claire" },
new TestObject { TestValue = "Matt" }
};
Imagine testList is actually millions of objects long.
What's the fastest way to ensure that two of those three TestObjects with TestValue of Matt gets its IsDuplicate set to true? No matter how may instances of a given value there are, only one should come out of the process with IsDuplicate of false.
I am not averse to doing this via threading. And the collection doesn't have to be a list if converting it to another collection type is faster.
I need to keep duplicates and mark them as such, not remove them from the collection.
To expand, this is (as you might imagine) a simple expression of a much more complex problem. The objects in question already have an ordinal which I can use to order them.
After matching initial duplicates on exact string equality, I'm going to have to go back through the collection again and re-try the remainder using some fuzzy matching logic. The collection that exists at the start of this process won't be changed during the deduplication, or afterwards.
Eventually the original collection is going to be written out to a file, with likely duplicates flagged.
As others mentioned, the correct approach here would be to use the HashSet class.
var hashSet = new HashSet<string>();
foreach (var obj in testList)
{
if (!hashSet.Add(obj.TestValue))
{
obj.IsDuplicate = true;
}
}
When you add a value first time to the HashSet, it adds successfully and HashSet.Add() method returns true so you don't make any changes to the item. When you're trying to add it second time, HashSet.Add() returns false and you mark your item as a duplicate.
The list will have the following state after finishing running our marking duplicates method:
Matt
Bob
Alice
Claire
Matt DUPLICATE
This is probably quite performant:
foreach (var dupe in testList.GroupBy(x => x.TestValue).SelectMany(g => g.Skip(1)))
dupe.IsDuplicate = true;
[EDIT] This method turns out to be about a third of the speed of the accepted answer above, so that one should be used. This answer is merely of academic interest.
Probably I would go to check for the duplicates while building the collection of the TestValue to avoid looping two times on millions of elements. If this scenario is possible then I would use a Dictionary<string, List<TestValue>>
Dictionary<string, List<TestValue>> myList = new Dictionary<string, List<TestValue>>();
while(NotEndOfData())
{
TestValue obj = GetTestValue();
if(myList.ContainsKey(obj.Name))
{
obj.IsDuplicate = true;
myList[obj.Name].Add(obj);
}
else
{
obj.IsDuplicate = false;
myList.Add(obj.Name, new List<TestValue>() { obj};
}
}
SortedSet<string> sorted = new SortedSet<string>();
for (int i = 0; i < testList.Count; i++)
testList[i].IsDuplicate = !sorted.Add(testList[i].TestValue);
As you have allowed in the question, I'd change testList to be an array instead of a list, to make indexer faster.
Since you indicated that you have a property that keeps the ordinal of your items. We can use that property to reset the sort order back to its original after marking our items as duplicates.
The code below is self-explainatory. But just let me know in case you need any further explaination.
I have assumed that the property name is SortOrder. Modify the code accordingly.
void MarkDuplicates()
{
testList = testList.OrderBy(f => f.TestValue).ThenBy(f => f.SortOrder).ToList();
for (int i = 1; i < testList.Count; i++)
{
if (testList[i].TestValue == testList[i - 1].TestValue) testList[i].IsDuplicate = true;
}
testList = testList.OrderBy(f => f.SortOrder).ToList();
}
I'm not a performance expert. But you can time the various solutions provided here and check the performance for yourself.
UPDATE: The initial idea was to nest the objects, but I then realised that having an ancestry trail in each object would suffice, so the title has been amended.
I want to build up a collection of objects which I can play with while developing (the idea will be to put them into a MongoDB collection) but can't figure out how to do the multiple depths.
The objects for this scenario have only a name, an id (auto incrementing), and a forward slash delimited string of each object's parents.
I have managed to get the initial recursion working:
id Ancestors Name
1
/
LFHVJULLCA
2
/1/
OHOEKOOFZP
3
/1/2/
PIUHBACFJV
4
/1/2/3/
PYKUFZOHXS
5
/1/2/3/4/
UJFUYENSQV
6
/1/2/3/4/5/
SHLMWCKZUJ
7
/1/2/3/4/5/6/
SFBHLQXTZL
8
/1/2/3/4/5/6/7/
AHHHUGDZIX
9
/1/2/3/4/5/6/7/8/
MKOODIMYXC
10
/1/2/3/4/5/6/7/8/9/
OONOZOHYJH
but what I want is to have a second or third level of ancestry, so that each item can have further children, updating its ancestry, and adding itself to the main collection.
And that is where I'm stuck. Any pointers please?
List<Thing> things;
int count;
void Main()
{
things = new List<Thing>();
count = 1;
int depth = 10;
int thingsPerThing = 3;
var thing = new Thing{ Ancestors = "/", id = count++, Name = "Name " + count};
things.Add(thing);
AddThing(depth, 0, thingsPerThing, thing);
things.Dump();
}
public class Thing
{
public int id{get;set;}
public string Ancestors{ get;set;}
public string Name{get;set;}
}
public void AddThing( int depth, int CurrentDepth, int thingsPerThing, Thing thing)
{
CurrentDepth++;
if( CurrentDepth < depth )
{
var newThing = CreateThing(thing);
things.Add( newThing );
AddThing( depth--, CurrentDepth, thingsPerThing, newThing);
}
}
public Thing CreateThing(Thing thing)
{
return new Thing{ Ancestors = thing.Ancestors + thing.id + "/", id = count++, Name = RandomString(10) };
}
You should use a Tree collection.
Some examples of implementation:
- An Extensive Examination of Data Structures Using C# 2.0 (MSDN)
- Implementing a Non-Binary Tree in C# (Dan Vanderboom's blog)
- A Generic Tree Collection (CodeProject)
To get the full path of a node you can add a Path property which walk backward to its root. To get the list of nodes (if needed) you have to implement a tree traversal algorithm, you can do it in many ways, which one to use depends on the order you want for your nodes (see link for details).
If you need a more sophisticated library for collection you may consider to check these:
- The C5 Generic Collection Library
- Wintellect's Power Collection