I have a raw csv data as mentioned below
James,Mary,Patricia,Anthony,Donald\n
145,10,100,39,101\n
21,212,313,28,1
In above mentioned string, columns are comma , separated, first line is column and after each \n a new row where the data is for each person. What I am trying to achieve here is it should be sorted as mentioned below.
Anthony,Donald,James,Mary,Patricia\n
39,101, 145,10,100\n
28,1,21,212,313
What I have tried so far is, splitting based on \n, further splitting based on comma , for each value, but in this case there will be no proper reference to sort value.
What Part I am Struggling with
string data = "James,Mary,Patricia,Anthony,Donald\n145,10,100,39,101\n21,212,313,28,1";
var rows = data.Split('\n');
var unorderedNames = rows[0].Split(',');
Split Array based on \n
Split Names based on , comma -
Now, If I implement sorting, I believe I will loose all references because names will be sorted but below in row 2 and 3, mentioned payments will not.
In my code mentioned above, The first line split the array into three based on \n. Then when I soft first line, I beleive I don't have reference of other values in the same array.
I appreciate if you can assist me to find some efficient method to convert this raw data into sored alphabetically with values in efficient way.
public class StackDemo
{
private string source = "James,Mary,Patricia,Anthony,Donald\n145,10,100,39,101\n21,212,313,28,1";
public string ProcessString()
{
var rows = source.Split('\n');
var row1Values = rows[0].Split(',');
var row2Values = rows[1].Split(',');
var row3Values = rows[2].Split(',');
List<Person> people = new List<Person>();
for (int index = 0; index < 5; index++)
{
people.Add(new Person()
{
Name = row1Values[index],
SomeValue = row2Values[index],
OtherValue = row3Values[index]
});
}
people.Sort((x, y) => x.Name.CompareTo(y.Name));
List<string> names = new List<string>();
List<string> someValues = new List<string>();
List<string> otherValues = new List<string>();
foreach (Person p in people)
{
names.Add(p.Name);
someValues.Add(p.SomeValue);
otherValues.Add(p.OtherValue);
}
string result = "";
result = BuildString(names, result);
result = BuildString(someValues, result);
result = BuildString(otherValues, result);
result = result.Remove(result.Length - 1, 1);
return result;
}
private static string BuildString(List<string> names, string result)
{
foreach (string s in names)
{
result += s + ",";
}
result = result.Remove(result.Length - 1, 1);
result += "\n";
return result;
}
}
public class Person
{
public string Name { get; set; }
public string SomeValue { get; set; }
public string OtherValue { get; set; }
}
This code is extremely basic, (rude) but it does what I think you want?)
Also it returns the string in the same format as it was received.
EDIT: Expanded on comment question!
Added some unit tests to help validate how I understood your question:
public class UnitTest1
{
[Fact]
public void TestWith5()
{
string input = "James,Mary,Patricia,Anthony,Donald\n145,10,100,39,101\n21,212,313,28,1";
string expected = "Anthony,Donald,James,Mary,Patricia\n39,101,145,10,100\n28,1,21,212,313";
// arrange
StackDemo3 subject = new StackDemo3();
// act
string actualResult = subject.ProcessString(input);
// assert
Assert.Equal(expected, actualResult);
}
[Fact]
public void TestWith4()
{
string input = "James,Mary,Patricia,Anthony,\n145,10,100,39,\n21,212,313,28,";
string expected = ",Anthony,James,Mary,Patricia\n,39,145,10,100\n,28,21,212,313";
// arrange
StackDemo3 subject = new StackDemo3();
// act
string actualResult = subject.ProcessString(input);
// assert
Assert.Equal(expected, actualResult);
}
[Fact]
public void TestWith3()
{
string input = "James,Mary,Patricia,,\n145,10,100,,\n21,212,313,,";
string expected = ",,James,Mary,Patricia\n,,145,10,100\n,,21,212,313";
// arrange
StackDemo3 subject = new StackDemo3();
// act
string actualResult = subject.ProcessString(input);
// assert
Assert.Equal(expected, actualResult);
}
[Fact]
public void TestWith2()
{
string input = ",,James,Mary,\n,,145,10,\n,,21,212,";
string expected = ",,,James,Mary\n,,,145,10\n,,,21,212";
// arrange
StackDemo3 subject = new StackDemo3();
// act
string actualResult = subject.ProcessString(input);
// assert
Assert.Equal(expected, actualResult);
}
[Fact]
public void TestWith1()
{
string input = "James,,,,\n145,,,,\n21,,,,";
string expected = "James,,,,\n145,,,,\n21,,,,";
// arrange
StackDemo3 subject = new StackDemo3();
// act
string actualResult = subject.ProcessString(input);
// assert
Assert.Equal(expected, actualResult);
}
[Fact]
public void TestWith0()
{
string input = ",,,,\n,,,,\n,,,,";
string expected = ",,,,\n,,,,\n,,,,";
// arrange
StackDemo3 subject = new StackDemo3();
// act
string actualResult = subject.ProcessString(input);
// assert
Assert.Equal(expected, actualResult);
}
}
Here is the actual implementation:
public interface IStringPeopleParser
{
List<Person> ConvertToPeople(string input);
}
public interface IPeopleStringParser
{
string ConvertPeopleToString(List<Person> people);
}
public class PeopleStringParser : IPeopleStringParser
{
public string ConvertPeopleToString(List<Person> people)
{
List<string> names = new List<string>();
List<string> someValues = new List<string>();
List<string> otherValues = new List<string>();
foreach (Person p in people)
{
names.Add(p.Name);
someValues.Add(p.SomeValue);
otherValues.Add(p.OtherValue);
}
string output = "";
output += string.Join(",", names);
output += "\n";
output += string.Join(",", someValues);
output += "\n";
output += string.Join(",", otherValues);
return output;
}
}
public class StringPeopleParser : IStringPeopleParser
{
public List<Person> ConvertToPeople(string source)
{
var rows = source.Split('\n');
string[] row1Values = rows[0].Split(',');
string[] row2Values = rows[1].Split(',');
string[] row3Values = rows[2].Split(',');
List<Person> people = new List<Person>();
for (int index = 0; index < row1Values.Length; index++)
{
people.Add(new Person()
{
Name = row1Values[index],
SomeValue = row2Values[index],
OtherValue = row3Values[index]
});
}
return people;
}
}
public class StackDemo3
{
IStringPeopleParser stringPeopleParser = new StringPeopleParser();
IPeopleStringParser peopleStringParser = new PeopleStringParser();
public string ProcessString(string s) {
List<Person> people = stringPeopleParser.ConvertToPeople(s);
int validCount = people.Where(x => x.IsValid()).Count();
switch (validCount)
{
case 0:
case 1:
{
return peopleStringParser.ConvertPeopleToString(people);
}
case 2:
case 3:
case 4:
case 5:
{
people = people.OrderBy(x => x.Name).ToList();
return peopleStringParser.ConvertPeopleToString(people);
}
default:
{
return "";//outside bounds of reality. Should never happen.
}
}
}
}
public class Person
{
public string Name { get; set; }
public string SomeValue { get; set; }
public string OtherValue { get; set; }
public bool IsValid() {
if (string.IsNullOrWhiteSpace(Name) || string.IsNullOrWhiteSpace(SomeValue) || string.IsNullOrWhiteSpace(OtherValue))
{
return false;
}
return true;
}
}
Also I don't really know why you don't want the person class?
You need to have a reference between the 3 values possible in each row (the index value is the key) by creating the Person class, the class instance becomes said reference.
I think the problem is you want to sort the headers of your CSV into some "whatever" order, and have the data "go with it"
Come up with some way to represent your data as a 2D array:
var lines = File.ReadAllLines("path");
var data = lines.Skip(1).Select(line => line.Split(',')).ToArray(); //nasty way of parsing a CSV but it's accessory to this discussion..
var head = lines[0]
.Split(',')
.Select((s,i) => new { Name = s, Index = i })
.OrderBy(at => at.Name)
.ToArray();
head is now the sorted headers, but it has an additional property that tells you what column in data holds that person's data. Anthony is first in heaD, but their Index is 3 so we should get Anthony's data from data[3]
foreach(var person in head){
Console.WriteLine($"Now printing {person.name} data from column {person.Index}");
foreach(var line in data){
Console.Writeline(line[person.Index]);
}
}
We didn't bother sorting the data (it's more efficient not to), we just stored what column it's in as part of the object that does get sorted, and then regardless of person sort order, we access the data via that column. Sorting head is very fast, because it's just a few names. It always maintains its map of "where is the data" because Index doesn't change regardless of the sort order of head
I have the following string:
string myString = "{'gridObject':'[1,2,3,4],[5,6,7,8]'}";
How do I process this into an object so that I can do this:
charts[0] //=> [1,2,3,4]
charts[0][1] //=> 2
If I can convert it to this object, even better:
public class gridObject {
public int datarow {get; set;}
public int datacol {get; set;}
public int datasizex {get; set;}
public int datasizey {get; set;}
}
This is what I would do.
Create your classes first,
public class GridObject
{
public int datarow { get; set; }
public int datacol { get; set; }
public int datasizex { get; set; }
public int datasizey { get; set; }
}
public class GridObjectCollection
{
public GridObject[] GridObjects { get; set; }
}
Then, to see what JSON you need, serialize it once: (JsonConvert is part of Json.NET, you can get it with NuGet)
GridObjectCollection gridObjects = new GridObjectCollection();
gridObjects.GridObjects = new GridObject[]
{
new GridObject() { datacol = 1, datarow = 2, datasizex = 3, datasizey = 4 },
new GridObject() { datacol = 5, datarow = 6, datasizex = 7, datasizey = 8 }
};
Console.WriteLine
(
JsonConvert.SerializeObject
(
gridObjects,
new JsonSerializerSettings() { Formatting = Formatting.Indented }
)
);
Here you can see that the valid JSON content which will produce these classes when deserialized is like:
{
"GridObjects": [
{
"datarow": 2,
"datacol": 1,
"datasizex": 3,
"datasizey": 4
},
{
"datarow": 6,
"datacol": 5,
"datasizex": 7,
"datasizey": 8
}
]
}
Then, just try a deserialization just to make sure:
var f = JsonConvert.DeserializeObject<GridObjectCollection>
(
"{'GridObjects':[{'datarow':2,'datacol':1,'datasizex':3,'datasizey':4},{'datarow':6,'datacol':5,'datasizex':7,'datasizey':8}]}"
);
Here is one way to do it:
public static gridObject[] Parse(string str)
{
int first = str.IndexOf("[");
int last = str.LastIndexOf("]");
str = str.Substring(first, last - first + 1);
string[] big_parts = str.Split(new string[] {"[", "],[", "]"} , StringSplitOptions.RemoveEmptyEntries);
return big_parts.Select(x =>
{
string[] small_parts = x.Split(',');
return new gridObject()
{
datarow = Convert.ToInt32(small_parts[0]),
datacol = Convert.ToInt32(small_parts[1]),
datasizex = Convert.ToInt32(small_parts[2]),
datasizey = Convert.ToInt32(small_parts[3]),
};
}).ToArray();
}
It first searches for the the first [ and the last ] and trims anything before [ and after ].
Then, it splits the string based on [, ],[, ].
This will give us 1,2,3,4 and 5,6,7,8 for your example.
Then for each one of them, we split based on , and convert the results into a gridObject object.
Done with custom parsing:
public class GridObject
{
public int datarow { get; set; }
public int datacol { get; set; }
public int datasizex { get; set; }
public int datasizey { get; set; }
}
/// <summary>
/// MySuperObject class which holds a reference to inner array of integers
/// </summary>
public class MySuperObject
{
public List<int> Items { get; set; } // Inner array of list of integers
public MySuperObject()
{
Items = new List<int>();
}
public override string ToString()
{
// Need to override ToString to return something like "[1,2,3,4]"
var result = "";
foreach (var item in Items)
{
if (result.Length > 0)
result += ",";
result += item.ToString();
}
return string.Format("[{0}]", result);
}
/// <summary>
/// Function to generate GridObject from existing set of integers
/// </summary>
public GridObject GetGridObject()
{
var result = new GridObject();
if (Items.Count >= 1) result.datarow = Items[0];
if (Items.Count >= 2) result.datacol = Items[1];
if (Items.Count >= 3) result.datasizex = Items[2];
if (Items.Count >= 4) result.datasizey = Items[3];
return result;
}
}
// Parse functions
public List<MySuperObject> Parse(string value)
{
if (string.IsNullOrEmpty(value))
throw new ArgumentException("value cannot be null or empty!", "value");
var result = new List<MySuperObject>();
// First get the indexes of first [ and last ]
var idxStart = value.IndexOf("[");
var idxEnd = value.LastIndexOf("]");
// Check validity
if (idxStart < 0 || idxEnd < 0 || idxEnd <= idxStart)
return result; // Return empty list
value = value.Substring(idxStart, idxEnd - idxStart + 1).Trim();
// Split by [] after replacing spaces with empty strings (and removing first and last [ and ])
var arr = value.Replace(" ", "").Trim('[',']').Split(new[] { "],[" }, StringSplitOptions.RemoveEmptyEntries);
foreach (var str in arr)
{
// Construct list of integers with a help of LINQ
var nums = str.Split(',').Select(t => Convert.ToInt32(t)).ToList();
// Create and add MySuperObject to existing list which will be returned
result.Add(new MySuperObject
{
Items = new List<int>(nums),
});
}
return result;
}
And here is the usage of such parsing:
var myString = "{'gridObject':'[1,2,3,4],[5,6,7,8]'}";
var value = Parse(myString);
// Get all grid objects
var allGridObjects = value.Select(t => t.GetGridObject()).ToList();
Of course, this could need a little bit more of error checking but basically, this MySuperObject is used to use any number of integers you desire, while providing you with a helper method of "GetGridObject" to fill you grid object with appropriate numbers from array of numbers.
This is my input store in file:
50|Carbon|Mercury|M:4;C:40;A:1
90|Oxygen|Mars|M:10;C:20;A:00
90|Serium|Jupiter|M:3;C:16;A:45
85|Hydrogen|Saturn|M:33;C:00;A:3
Here 50,90,90,85 indicates weight and M,C,A represents Proportions in each of this element.
Now i want to display each elements(i.e Carbon,Oxygen etc) from highest to lowest and if there are multiple elements with the same weights,group them under a single Weight and sort them alphabetically by Planet(mars,jupiter) and then Elements(carbon,Oxygen etc..)
Expected output:
1)90
Serium;Jupiter (sorted alphabetically by planet name).
compounds:M:3;C:16;A:45
Oxygen;Mars
compounds:M:10;C:20;A:00
2)85
Hydrogen;Saturn
M:33;C:00;A:3
3)50
Carbon;Mercury
M:4;C:40;A:1
This is how i have done:
public class Planets
{
public int Number { get; set; } //This field points to first cell of every row.output 50,90,90,85
public string name { get; set; } //This field points to Second cell of every row.output Hallogen,Oxygen,Hydrogen
public string object { get; set; } ////This field points to third cell of every row.output Mercury,Mars,Saturn
public List<proportion> proportion { get; set; } //This will store all proportions with respect to planet object.
//for Hallogen it will store 4,40,1.Just store number.ignore M,C,A initials.
//for oxygen it will store 10,20,00.Just store number.ignore M,C,A initials.
}
public class proportion
{
public int Number { get; set; }
}
List<Planets> Planets = new List<Planets>();
using (StreamReader sr = new StreamReader(args[0]))
{
String line;
while ((line = sr.ReadLine()) != null)
{
Planets planet = new Planets();
String[] parts = line.Split('|');
planet.Number = Convert.ToInt32(parts[0]);
planet.name = parts[1];
planet.obj = parts[2];
String[] smallerParts = parts[3].Split(';');
planet.proportion = new List<proportion>();
foreach (var item in smallerParts)
{
proportion prop = new proportion();
prop.Number =
Convert.ToInt32(item.Split(':')[1]);
planet.proportion.Add(prop);
}
Planets.Add(planet);
}
}
var data = Planets.OrderByDescending(t => t.Number).ToList();//Highest to lowest.
foreach (var item in data)
{
//What to do for same elements
}
I am successfully able to add all 4 row in my planet list object like this:
Planets[0]:
{
Number:50
name: Carbon
object:Mercury
proportion[0]:
{
Number:4
},
proportion[1]:
{
Number:40
},
proportion[2]:
{
Number:1
}
}
Etc.......
Here only problem i am getting is for displaying weight of same number(Expected output 1) and alphabetically sorting by Planet(mars,jupiter) and then by Elements(carbon,Oxygen etc..)
I have created the following code on LinqPad to achieve the result that you are expecting, Let me know if you still need changes, a simple Linq query will help in achieving the result:
void Main()
{
List<Input> customList = Input.Create();
var result = customList.GroupBy(x=>x.Weight,x=>new {x.Element1,x.Element2,x.Detail})
.Select(y=>new {
key = y.Key,
collection = y.OrderBy(z=>z.Element2)
}
).OrderByDescending(h=>h.key);
foreach(var n in result)
{
Console.WriteLine("Weight :: " + n.key);
foreach(var g in n.collection)
{
Console.WriteLine(g.Element1 + ";" + g.Element2);
Console.WriteLine("Compounds:" + g.Detail);
}
}
}
public class Input
{
public int Weight {get; set;}
public string Element1 {get; set;}
public string Element2 {get; set;}
public string Detail {get; set;}
public Input(int w, string e1, string e2, string d)
{
Weight = w;
Element1 = e1;
Element2 = e2;
Detail = d;
}
public static List<Input> Create()
{
List<Input> returnList = new List<Input>();
returnList.Add(new Input(50,"Carbon","Mercury","M:4;C:40;A:1"));
returnList.Add(new Input(90,"Oxygen","Mars","M:10;C:20;A:00"));
returnList.Add(new Input(90,"Serium","Jupiter","M:3;C:16;A:45"));
returnList.Add(new Input(85,"Hydrogen","Saturn","M:33;C:00;A:3"));
return (returnList);
}
}
Here is my code in the controller.
public JsonResult directory()
{
List<string> alp = new List<string>();
var alp1 = new List<directories>();
string array = "";
int a = 0;
for (a = 0; a <= 25; a++)
{
int unicode = a + 65;
char character = (char)unicode;
string text1 = character.ToString();
string url1 = "<a href='/Directories/?search=" + text1 + "' 'rel=" + text1 + "'>";
string alpha = text1;
alp.Add(url1);
alphatxt.Add(alpha);
}
var alphaa = alp1.Add(new directories { arrary = url1, character = alphatxt });
return Json(alphaa, JsonRequestBehavior.AllowGet);
}
public class directories
{
public int a { get; set; }
public int unicode { get; set; }
public char character { get; set; }
public string[] arrary { get; set; }
}
Outputs are getting by
alp.Add(url1);
alp.Add(alpha);
How can i call these two outputs outside the loop.
so that i will get my output through the return
Json(alphaa, JsonRequestBehavior.AllowGet);
But I dont know how to declare the output to the variable outside the loop.
If you are trying to build a list of urls, one for each letter, then you can simply do something like:
public List<Directory> GetDirectories()
{
var dirs = new List<Directory>();
for (var ch = 'A'; ch <= 'Z'; ch++)
{
var url = string.Format(
"<a href='/Directories/?search={0}' rel='{0}'>", ch);
dirs.Add(new Directory() { Character = ch, Url = url });
}
return dirs;
}
// Directory class is simplifed a bit in this example
public class Directory
{
public char Character { get; set; }
public string Url { get; set; }
}
And then simply convert it to JSON in a separate method:
public JsonResult directory()
{
var dirs = GetDirectories();
return Json(dirs, JsonRequestBehavior.AllowGet);
}
Using LINQ, it could be simplified to:
private static readonly string Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
public List<Directory> GetDirectories()
{
return Alphabet
.Select(ch => new Directory() { Character = ch, Url = CreateUrl(ch) })
.ToList();
}
private string CreateUrl(char ch)
{
return string.Format("<a href='/Directories/?search={0}' 'rel={0}'>", ch);
}
The way your code looks right now, it doesn't seem like you need to create this list on the server side at all (you are transferring a bunch of almost equal hard-coded URLs, which can easily be created on the client side using JavaScript), so I presume there is some additional data you are transferring with this query?
You can access the JsonResult.Data property, but I don't really think that is what you need. I suggest to create a method that return the actual result, and inside your action you call that one and serialize it as JSON:
public JsonResult directory()
{
return Json(this.GetDirectories(), JsonRequestBehavior.AllowGet);
}
private List<directories> GetDirectories()
{
... // your original code
}
I tried in many ways, At last got idea to use this way,
Also My code is shown below.
public JsonResult directory()
{
List<string> alp = new List<string>();
var alp1 = new List<directories>();
string array = "";
int a = 0;
for (a = 0; a <= 25; a++)
{
int unicode = a + 65;
char character = (char)unicode;
string text1 = character.ToString();
string url1 = "<a href='/Directories/?search=" + text1 + "' 'rel=" + text1 + "'>";
string alpha = text1;
alp.Add(url1);
alp.Add(alpha);
alp1.Add(new directories { dirurl = url1, text = alpha });
}
return Json(alp1, JsonRequestBehavior.AllowGet);
}
public class directories
{
public string text { get; set; }
public string dirurl { get; set; }
}
}
}