Fast save\load big data - c#

I have the following class:
public class HPChartGraphPoint
{
public int Keyword { get; set; }
public List<long> Points { get; set; }
public HPChartGraphPoint()
{
Points = new List<long>();
}
public string SerializeToString()
{
var sb = new StringBuilder();
sb.Append(Keyword);
sb.Append(":");
for (int i = 0; i < Points.Count; i++)
{
sb.Append(Points[i]);
sb.Append(":");
}
return sb.ToString();
}
public static HPChartGraphPoint DesirializeFromString(string str)
{
var splitter = str.Split(new[]{":"},StringSplitOptions.RemoveEmptyEntries)
.Select(x => int.Parse(x)).ToArray();
var retPoint = new HPChartGraphPoint();
retPoint.Keyword = splitter[0];
for (int i = 1; i < splitter.Length; i++)
{
retPoint.Points.Add(splitter[i]);
}
return retPoint;
}
}
i need to store and load a collection List with millions elements. How to do it fast and memory efficient?
EDIT:
Here is code for storing
using (var sw = new StreamWriter(filePath))
{
for (int i = 0; i < HPChartGraphPointCollection.Count; i++)
{
sw.WriteLine(HPChartGraphPointCollection[i].SerializeToString());
}
}

Binary format could be more compact and easier to read/parse than text.
You could use light-weight stream wrappers: BinaryReader and BinaryWriter.

Related

How do I connect an IObservableList<double[]> to a list of objects with an IObservableList<double> property

I want to read data from a file or an analog digital converter (ADC) and convert the data with the help of the dynamicdata library. Suppose I read blocks of 2d matrices of ushort numbers from this file or ADC.
In the example below I add a 2d matrix to the FileNode class and that is converted to an IObservableList<double[]> list. Now I want to connect this to the Data property of the SampledSignal class which is of type IObservableList<double>. Does anyone have any idea how I should do this?
public class SampledSignal
{
public SampledSignal(IObservableList<double> data, double sr)
{
Data = data;
Samplerate = sr;
}
public IObservableList<double> Data { get; private set; }
public double Samplerate { get; }
}
public class FileNode : ReactiveObject
{
readonly IObservable<IEnumerable<ushort[]>> _currentDataObsv;
const int _signalCount = 4;
readonly private SampledSignal[] _sampledSignals;
public FileNode()
{
_sampledSignals = Enumerable.Range(0, _signalCount)
.Select(s => new SampledSignal(null, 200)).ToArray();
CurrentData = Enumerable.Empty<ushort[]>();
_currentDataObsv = this.WhenAnyValue(x => x.CurrentData);
IObservableList<double[]> dblArList = _currentDataObsv
.ToObservableChangeSet()
.TransformMany(d => ToEnumerable(d))
.AsObservableList();
// The lines below are just some code to give an idea of what I want
for (int i = 0; i < _signalCount; i++)
{
// maybe here an observablelist should be created?
// IObservableList<double> data = ??
// _sampledSignals[i] = new SampledSignal(data, 200);
}
OutSignals=_sampledSignals;
}
public IEnumerable<double[]> ToEnumerable(ushort[] array)
{
double offset = 0;
double amp = 4;
int len = array.Length;
int cnt = 0;
double[] x = new double[len];
for (int i = 0; i < len; i++)
{
x[i] = Convert.ToDouble(array[i] - short.MaxValue) / ushort.MaxValue * amp + offset;
}
cnt++;
yield return x;
}
[Reactive] public IEnumerable<ushort[]> CurrentData { get; set; }
// maybe this property can also be an IObservableList<SampledSignal> OutSignals
public IEnumerable<SampledSignal> OutSignals { get; private set; }
}
[Fact]
public void Test_SampledData()
{
int signalcount = 20;
IEnumerable<ushort[]> array = Enumerable.Range(0, 4)
.Select(j =>
{
var x = new ushort[signalcount];
for (int i = 0; i < x.Length; i++)
{
x[i] = Convert.ToUInt16(short.MaxValue+(i+j)*4);
}
return x;
});
var filenode = new FileNode();
filenode.CurrentData = array;
// do some testing below
SampledSignal signal = filenode.OutSignals.First();
signal.Should().NotBeNull();
signal.Data.Count.Should().Be(signalcount);
}
I have created a solution that works but I don't like it very much because I have to create a SourceList class in the SampledData class.
I was hoping for a solution were I directly could set the IObservable<double> Data property of the SampledData class.
// The SampledSignal class now looks like this.
public class SampledSignal
{
private readonly ISourceList<double> _sourceList = new SourceList<double>();
public SampledSignal(IObservableList<double> data, double sr)
{
Data = _sourceList;
Samplerate = sr;
}
public ISourceList<double> Data { get; private set; }
public double Samplerate { get; }
}
// The FileNode constructor now looks like this
public FileNode()
{
_sampledSignals = Enumerable.Range(0, _signalCount)
.Select(s => new SampledSignal(null, 200)).ToArray();
CurrentData = Enumerable.Empty<ushort[]>();
_currentDataObsv = this.WhenAnyValue(x => x.CurrentData);
IObservableList<double[]> dblArList = _currentDataObsv
.ToObservableChangeSet()
.TransformMany(d => ToEnumerable(d))
.AsObservableList();
dblArList.Connect().ToCollection()
.Subscribe(sigs =>
{
int cnt = 0;
foreach (var sig in sigs)
{
var regdata = _sampledSignals[cnt++];
regdata.Data.Edit(innerlist =>
{
innerlist.Clear();
innerlist.AddRange(sig);
});
}
});
OutSignals=_sampledSignals;
}

Reading string from a file to 2d array

i'm trying to convert a string from file to 2d array. Is it possible to make the array dynamic? And how can i return the array, so i can work with it? I'm really confused.
public interface IMazeLoader
{
Maze LoadMaze(string src);
}
class MazeLoader : IMazeLoader
{
public Maze LoadMaze(string filepath)
{
var width = 5;
var height = 5;
var map = new char[width, height];
var file = new StreamReader(filepath);
string line;
var lineCount = 0;
while ((line = file.ReadLine()) != null)
{
if (lineCount < height)
{
for (int i = 0; i < width && i < line.Length; i++)
{
map[i, lineCount] = line[i];
}
}
lineCount++;
}
file.Close();
return;
}
}
Here is my Maze class :
public class Maze
{
public static readonly char CHAR_WALL = '#';
public static readonly char CHAR_START = 'S';
public static readonly char CHAR_FINISH = 'F';
public int Width { get; internal set; }
public int Height { get; internal set; }
public int StartX { get; private set;}
public int StartY { get; private set; }
public int StartAngle { get; private set; }
private char[,] _plan;
public char[,] Plan { get { return _plan; } }
public Maze(char[,] plan)
{
}
Thanks for any reply, happy Eastern btw. :)
Something like this should allow you to load any rectangular dimension map:
public class MazeLoader : IMazeLoader
{
public Maze LoadMaze(string filepath)
{
char[][] loaded =
File
.ReadLines(filepath)
.Select(x => x.ToCharArray())
.ToArray();
int[] widths =
loaded
.Select(x => x.Length)
.Distinct()
.ToArray();
if (widths.Length != 1)
{
throw new InvalidDataException("Map Data Not Rectangular");
}
else
{
var width = widths[0];
var height = loaded.Length;
var map = new char[height, width];
for (int i = 0; i < height; i++)
for (int j = 0; j < width; j++)
map[i, j] = loaded[i][j];
return new Maze(map);
}
}
}
Now I tested this on this input:
aaaa
aaaa
bbbb
bbbb
bbbb
And got this map:
Please note that the file orientation and the map orientation are the same. I had to flip around how you were building our your map to get that to work.
Here's my alterations to Maze itself:
public class Maze
{
public int Width => this.Plan.GetUpperBound(1) - this.Plan.GetLowerBound(1) + 1;
public int Height => this.Plan.GetUpperBound(0) - this.Plan.GetLowerBound(0) + 1;
public char[,] Plan { get; private set; }
public Maze(char[,] plan)
{
this.Plan = plan;
}
}

getting a performance hit for nested for loop in C#

I have an array of string, a total of(100k). I need to check if the same string has occurred previously, if it occurs all I have to do is return that string. I have written the code using nested for loops, but unfortunately I am getting bad performance. It takes 1.9 mins to process the function correctly for (string[100K]) whereas I am expecting it to finish it within a couple of seconds.
My concern is not the logic. My concern is the LOOP. How to increase the efficiency of the loop.
public string StartMatchingProcess(string[]inputArray)
{
string[] stringArray = inputArray;
string result = string.Empty;
for (long i = 0; i < stringArray.Length; i++)
{
for (long j = 0; j <= i; j++)
{
if(i == j) break;
if (IsPrefix(stringArray[i], stringArray[j]))
{
return stringArray[i];
}
}
}
Console.WriteLine("GOOD SET");
return result;
}
public bool IsPrefix(string string1,string string2)
{
if (AreString1ValuesValid(string1, string2))
{
if (string1 == string2.Substring(0, string1.Length))
{
Console.WriteLine("BAD SET");
Console.WriteLine(string1);
return true;
}
}
else if (AreString2ValuesValid(string1, string2))
{
if (string2 == string1.Substring(0, string2.Length))
{
Console.WriteLine("BAD SET");
Console.WriteLine(string1);
return true;
}
}
return false;
}
public bool AreString1ValuesValid(string string1, string string2)
=> string1.Length <= string2.Length;
public bool AreString2ValuesValid(string string1, string string2)
=> string2.Length <= string1.Length;
Sort the initial array, and you can check neighbors only:
public string StartMatchingProcess(string[] inputArray) {
if (null == inputArray)
throw new ArgumentNullException(nameof(inputArray));
string[] sorted = inputArray.OrderBy(item => item).ToArray();
for (int i = 1; i < sorted.Length; ++i) {
string prior = sorted[i - 1];
string current = sorted[i];
if (current.StartsWith(prior))
return prior;
}
return "";
}
So, you'll have O(n * log(n)) time complexity vs. O(n**2) (initial solution)
It's really bad idea to use nested loops for this task because you have O(n*n) complexity for the answer and need to make 10.000.000.000 calls of Substring() for 100k array.
There is a specific structures for strings. For example, you can use Trie:
public string StartMatchingProcess(string[] inputArray)
{
var trie = new Trie();
foreach(var w in inputArray)
trie.AddWord(w);
foreach(var w in inputArray)
if(trie.HasPrefix(w) || trie.HasWord(w)
return w;
return string.Empty;
}
If you are just trying to determine if your array has duplicate string values, consider LINQ to get the count of the occurences.
string[] arrayTest = new string[] { "hello", "hello", "world"};
string myValue = "hello";
var stringCount = arrayTest.Where(n => n == myValue).Count();
if (stringCount > 1) return myValue;
In the above, we check to see if "hello" is in the array more than once, and if it is, we return it.
Here is a complete solution using linq.
public class Node
{
public char letter { get; }
public int Index { get; set; }
public List<Node> ChildList { get; set; } = new List<Node>();
public Node(char item, int index)
{
Index = index;
letter = item;
}
}
public class NoPrefixSet
{
public Dictionary<char, Node> ParentNode { get; set; } = new Dictionary<char, Node>();
public string GenerateNodes(string[] inputArray)
{
for (int i = 0; i < inputArray.Length; i++)
{
if (IsWordPrefix(inputArray[i]))
{
Console.WriteLine("BAD SET");
Console.WriteLine(inputArray[i]);
return inputArray[i];
}
}
Console.WriteLine("Good Set");
return "Good Set";
}
private void InsertNodeInParent(char item)
=> ParentNode.Add(item, new Node(item, 0));
private bool IsWordPrefix(string word)
{
//Check parent
Node parentNode = null;
bool hasNotInserted = false;
int similarCounter = 0;
if (!ParentNode.Any(a => a.Key == word[0]))
{
InsertNodeInParent(word[0]);
}
parentNode = ParentNode.Where(a => a.Key == word[0]).FirstOrDefault().Value;
for (int letterIndex = 0; letterIndex < word.Length; letterIndex++)
{
if (!parentNode.ChildList.Any(a => a.letter == word[letterIndex]))
{
parentNode.ChildList.Add(new Node(word[letterIndex], letterIndex));
}
else
{
if (!parentNode.ChildList.Where(a => a.letter == word[letterIndex]).First().ChildList.Any() || word.Length == letterIndex+1)
{
if (similarCounter == letterIndex)
return hasNotInserted = true;
}
similarCounter++;
}
parentNode = parentNode.ChildList.Where(a => a.letter == word[letterIndex] && a.Index == letterIndex).First();
}
return hasNotInserted;
}
public void ReadInput()
{
long data = Convert.ToInt64(Console.ReadLine());
string[] stringArray = new string[data];
for (long i = 0; i < data; i++)
{
stringArray[i] = Console.ReadLine();
}
GenerateNodes(stringArray);
}
}

How to serialize/deserialize an object matrix to/from xml in C#?

I have an assignment to save and load a Minesweeper board. I'm having issues saving the board matrix. These are my Minesweeper properties:
[XmlIgnore]
public Tile[,] Grid { get; set; }
public int Width { get; set; }
public int Height { get; set; }
public int NumberOfMines { get; set; }
Tile properties:
public int X { get; set; }
public int Y { get; set; }
public int NeighbourMines { get; set; }
public bool IsMine { get; set; }
public bool IsRevealed { get; set; }
public bool IsFlagged { get; set; }
I tried something like this:
public List<Tile> ListFromMatrix
{
get
{
List<Tile> temp = new List<Tile>(Height * Width);
for (int i = 0; i < Height; i++)
for (int j = 0; j < Width; j++)
temp.Add(Grid[i, j]);
return temp;
}
set
{
//Grid = new Tile[Height, Width];
int it = 0;
for (int i = 0; i < Height; i++)
for (int j = 0; j < Width; j++)
Grid[i, j] = value[it++];
}
}
Saving into the file works fine, but loading from it causes an exception. And I really don't know how to debug it since the exception is thrown at:
//this.Game is the Minesweeper reference in the main form
this.Game = (Game)xs.Deserialize(fileStream);
Any help is appreciated!
EDIT: This is the exception
System.InvalidOperationException: 'There is an error in XML document (7, 4).
Inner Exception 1: NullReferenceException: Object reference not set to an instance of an object.
EDIT2: Save code
SaveFileDialog sfd = new SaveFileDialog();
if (sfd.ShowDialog() == DialogResult.OK)
{
using (FileStream fs = new FileStream(sfd.FileName, FileMode.Create))
{
XmlSerializer xs = new XmlSerializer(typeof(Game));
xs.Serialize(fs, this.Game);
}
}
EDIT3: Here are the xml contents https://pastebin.com/0vkxQC5A
EDIT4: Thank you for trying but nothing works with this so I will just rewrite the code to use a list instead of the matrix.
You might try changing your set like so:
set
{
var height = value.Max(t=>t.Y);
var width = value.Max(t=>t.X);
Grid = new Tile[height, width];
foreach(var tile in value)
{
Grid[tile.Y,tile.X]=tile;
}
}
You probably don't want to use the height and width properties from the game object, because then you would need to assume those get set prior to this property. Might as well just calculate them yourself. And while you are at it, might as well just change get as well, and then throw away Height/Width, or change them to actually pull the current width/height of the Grid:
get
{
var temp = new List<Tile>(Grid.Length);
for (int i = 0; i < Grid.GetLength(0); i++)
for (int j = 0; j < Grid.GetLength(1); j++)
temp.Add(Grid[i, j]);
return temp;
}
It appears the error is in deserializing your property ListFromMatrix.
public List<Tile> ListFromMatrix {get; set;}
// when it comes time to serialize
ListFromMatrix = CreateList();
using (FileStream fs = new FileStream(sfd.FileName, FileMode.Create))
{
XmlSerializer xs = new XmlSerializer(typeof(Game));
xs.Serialize(fs, this.Game);
}
private List<Tile> CreateList()
{
var temp = new List<Tile>(Height * Width);
for (int i = 0; i < Height; i++)
for (int j = 0; j < Width; j++)
temp.Add(Grid[i, j]);
return temp;
}

ObservableCollection binary serialization and transfer over the network

There is a small problem ka. there is a class
public class PLayer
{
public String Name{get;set;}
public TimeSpan Tax { get; set; }
}
The main form
public partial class MainWindow : Window
{
public ObservableCollection<PLayer> PlayersInGame { get; set; }
public ObservableCollection<PLayer> PlayersInGame2 { get; set; }
public ObservableCollection<PLayer> PlayersOnBench { get; set; }
public MainWindow()
{
PlayersInGame = new ObservableCollection<PLayer>();
PlayersInGame2 = new ObservableCollection<PLayer>();
PlayersOnBench = new ObservableCollection<PLayer>();
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
for (int i = 0; i < 10; i++)
{
String vName = "Игрок" + i.ToString();
PlayersInGame.Add(new PLayer { Name = vName, Tax = new TimeSpan(0) });
}
for (int i = 10; i < 20; i++)
{
String vName = "Игрок" + i.ToString();
PlayersInGame2.Add(new PLayer { Name = vName, Tax = new TimeSpan(0) });
}
Game.Items.Refresh();
}
private void Button2_Click(object sender, RoutedEventArgs e)
{
if (Game.SelectedIndex > -1)
{
var temp = PlayersInGame[Game.SelectedIndex];
//PlayersInGame.RemoveAt(Game.SelectedIndex);
temp.Tax = new TimeSpan(0, 0, 5);
PlayersOnBench.Add(temp);
Game.Items.Refresh();
Bench.Items.Refresh();
}
if (Game2.SelectedIndex > -1)
{
var temp = PlayersInGame2[Game2.SelectedIndex];
//PlayersInGame2.RemoveAt(Game2.SelectedIndex);
temp.Tax = new TimeSpan(0, 0, 5);
PlayersOnBench.Add(temp);
Game2.Items.Refresh();
Bench.Items.Refresh();
}
}
private void timer_Tick(object sender, EventArgs e)
{
foreach (var x in PlayersOnBench)
{
x.Tax -= new TimeSpan(0, 0, 1);
}
List<int> Temp = new List<int>();
for (var i = 0; i < PlayersOnBench.Count; i++)
{
if (PlayersOnBench[i].Tax == TimeSpan.Zero)
{
Temp.Add(i);
}
}
for (int i = Temp.Count - 1; i >= 0; i--)
{
var s = PlayersOnBench[i];
PlayersOnBench.RemoveAt(Temp[i]);
//PlayersInGame.Add(s);
//Game.Items.Refresh();
}
Bench.Items.Refresh();
}
}
On the main form when you click on the button "Button2_Click" line is added to the ListView "Bench" with the addition of a timer. in the treatment of "timer_Tick" The timer is counting all the lines added to the "Bench". Contact ossushestvlyaetsya a Binding. My question is knowing binary serialization, how to transfer the contents of ListView "Bench" to the server to display in a ListView or ListBox. The binary serialization of the project has been in use for sending text fields.
Your question is kind of unclear on what you trying to achieve. In general, if you using binary serialization, it will convert your objects into byte array, you need to de-serialize inorder to get your object back. Below is a sample
BinaryFormatter m_formatter;
Byte[] m_stateData;
List<T> cloned_objList;
public binaryserializer(List<T> PlayersOnBench)
{
if ((!Object.ReferenceEquals(listToClone, null)) && (typeof(T).IsSerializable))
{
m_formatter = new BinaryFormatter();
using (MemoryStream stream = new MemoryStream())
{
try
{
m_formatter.Serialize(stream, PlayersOnBench);
}
catch { }
stream.Seek(0, SeekOrigin.Begin);
m_stateData = stream.ToArray();
}
}
}
public List<T> BenchStates
{
get
{
using (MemoryStream stream = new MemoryStream(m_stateData))
{
try
{
cloned_objList = (List<T>)m_formatter.Deserialize(stream);
}
catch (Exception) { }
}
return cloned_objList;
}
}

Categories

Resources