Laden...

Zwei Zahlenlisten abgleichen, fehlende Einträge finden und ausgeben

Erstellt von Santana vor 8 Jahren Letzter Beitrag vor 8 Jahren 2.507 Views
S
Santana Themenstarter:in
11 Beiträge seit 2015
vor 8 Jahren
Zwei Zahlenlisten abgleichen, fehlende Einträge finden und ausgeben

Hallo, ich hoffe das ist hier richtig.
Folgendes Problem. Ich habe zwei Zahlenlisten im Format:
54030;720;34;64929;930;12;30000;120;3;...

Das heißt die Information über ein Objekt hat immer 3 Zahlen zb: "54030;720;34"
Also sind immer alle dritten Einträge miteinander vergleichbar.

Mein Programm soll nun diese beiden Listen abgleichen, also für jede 3. Zahl (also erstmal die fünfstelligen) aus der einen Liste alle 3. Zahlen aus der anderen Liste anschauen und feststellen ob sie übereinstimmen. Ist das der Fall, wird die zweite (also der Eintrag im Feld dahinter) überprüft und danach der dritte Eintrag. Stimmen alle Zahlen überein, gilt das Objekt als gefunden. Alle Objekte, die nicht gefunden wurden, sollen in einer Liste mit dem Format:

54030;720;34
64929;930;12
30000;120;3
...

ausgegeben werden.

Leider funktioniert mein Algorithmus dafür noch nicht. Könnt ihr ihn euch mal anschauen und mir Verbesserungsvorschläge geben?

Hinweis: Soll[] ist das Feld mit mehr Objekten. Es sind quasi die, die hätten gefunden werden sollen
Ist[] sind die Objekte, die tatsächlich gefunden wurden. Die Felder haben auch die entsprechenden Einträge. Nur der Abgleich liefert keine fehlenden Objekte, was auf jeden Fall nicht sein kann.

//Vergleich!
			i=0;
			int j=1;
			StreamWriter fl = new StreamWriter("../../../../final.txt");
			int[] Merker = new int[Zahlenfeld2.Length];
			
			while(i<Merker.Length)
			{
				Merker[i]=0;
				i++;
			}
			i=1;
			while(i<Zahlenfeld2.Length)
			{
				while(j<Zahlenfeld1.Length)
				{
					if((i+2)%3==0 && (j+2)%3==0)
					{
					if (Soll[i]==Ist[j])
					{
						if(Soll[i+1]==Ist[j+1])
						{
							if(Soll[i+2]==Ist[j+2])
							{
						Merker[i]=i;
							}
						}
					}
					}
					j++;
				}
				i++;
			}
			i=1;
			Console.WriteLine("Fehlende Spektren: ");
			while(i<Merker.Length)
			{
				if((i+2)%3==0)
				{
					if(Merker[i] != 0)
					{
					Console.WriteLine(Soll[i]+";"+Soll[i+1]+";"+Soll[i+2]);
					fl.WriteLine(Soll[i]+";"+Soll[i+1]+";"+Soll[i+2]);
					}
				}
				i++;
			}
			fl.Flush();
        	fl.Close();
        	Console.ReadLine();

W
955 Beiträge seit 2010
vor 8 Jahren

Hi,

wenn niemals Leerzeichen zwischen den Elementen einer Menge auftrten kannst du doch einfach die Strings vergleichen oder? Mit Linq's Except wäre das dann vllt 2-3 Zeilen Code.

2.207 Beiträge seit 2011
vor 8 Jahren

Ich habs mal vom Code-Review ins passendere Forum verschoben. Bitte beachte unsere Regeln für das Review-Forum.

Code-Review Regeln

Was genau funktioniert denn nicht? Wenn du dir Objekte machen würdest, nach OOP-Ansatz, mit beispielsweise den Properties Digit1, 2 und 3, dann kannst du viel besser vergleichen, evtl. sogar einen Comparer schreiben dafür. Dann noch mit LINQ und es sollte nicht allzu schwierig werden 😉

Wenn du den LINQ-Ansatz nicht verfolgen willst sind kleine gut benannte Methoden bei solchen Aufgabestellungen meist sehr hilfreich.

Bitte beachte auch hier
[Artikel] Debugger: Wie verwende ich den von Visual Studio?

S
Santana Themenstarter:in
11 Beiträge seit 2015
vor 8 Jahren

Wie meinst du das? Die Reihenfolge der Objekte in den beiden Listen stimmt ja nicht überein. Also es muss schon erstmal alle 5-stelligen Zahlen verglichen werden, so wie ich das sehe und dann weiter machen. Sicher könnte ich auch direkt die Strings verglichen, das stimmt. Aber das ist ja nicht das entscheidende Problem oder?

Was genau funktioniert denn nicht? Wenn du dir Objekte machen würdest, nach OOP-Ansatz, mit beispielsweise den Properties Digit1, 2 und 3, dann kannst du viel besser vergleichen, evtl. sogar einen Comparer schreiben dafür. Dann noch mit LINQ und es sollte nicht allzu schwierig werden 😉.

Danke! Ehrlich gesagt, hab ich keine Ahnung was LINQ und dieser OOP-Ansatz ist.
Eigentlich bin ich ja auch der Meinung, dass das was ich hab funktionieren müsste. Ich dachte, jemand der mehr Erfahrung hat, kann mir da vielleicht sagen, woran es hapert.

Was nicht funktioniert: Eigentlich müssten in meinem Beispiel 2 Objekte ausgegeben werden, die nicht gefunden wurden. Allerdings werden gar keine angezeigt. Bis zu der Codestelle, die ihr seht, funktioniert das Programm einwandfrei, also hier muss irgendwo ein Fehler liegen.

Problem gelöst! Hatte in der i-schleife vergessen "j=1;" hinzuschreiben. Somit wurde die j-Schleife ja nur einmal durchlaufen und nichts gefunden.
Danke trotzdem.

Hinweis von Coffeebean vor 8 Jahren

Bitte beachte [Hinweis] Wie poste ich richtig? Punkt 2.3

C
224 Beiträge seit 2009
vor 8 Jahren

Kannst du noch mal schöner beschreiben, was du Vergleichen willst?

So in der Form etwa:

123; 456; 789; 123; 456; 789;
123; 456; 789; 123; 456; 789;

Dein Merker finde ich ungünstig. Stattdessen würde ich eine List verwenden. z.B.:




List<string> nichtGefundenListe = new List<string>();

//Such-Schleife
.. 
bool nichtGefunden = ...
...
if (nichtGefunden)
{
  nichtGefundenListe.Add("123; 456; 789; 123; 456; 789");//<-- string durch gefundenes Element ersetzen 
}
...

//Ausgabe
foreach(string eintrag in nichtGefundenListe)
{
     Console.WriteLine(eintrag);
}

Statt Deiner while-Schleifen würde ich doch eher for(i..-Schleifen oder foreach-Schleifen verwenden.

W
955 Beiträge seit 2010
vor 8 Jahren

Na dann mach doch
* Schreib eine Struct mit drei int-Properties
* zerhacke die Kette mit String.Split und ordne immer drei nachfolgende Werte diesen Structs zu (vllt ein .ctor der drei Strings aufnimmt, int's daraus parst und sie den Properties zuweist)
* du solltest dann zwei Listen mit diesen Werten haben
* dann mit Linq's Except arbeiten

3.003 Beiträge seit 2006
vor 8 Jahren

//deine zwei ursprünglichen Arays heissen bei mir stringinput1, stringinput2

const string FILEPATH = @"pathToFile";

var input1 = stringinput1.Split(';').Select(p => Convert.ToInt32(p));
var input2 = stringinput2.Split(';').Select(p => Convert.ToInt32(p));


var list1 = input1.Where((p,index) => index %3 == 1).Select((p,index) => new { FirstValue = p, SecondValue = input1[3*index+1], ThirdValue = input1[3*index+2] });
var list2 = input1.Where((p, index) => index % 3 == 1).Select((p, index) => new { FirstValue = p, SecondValue = input1[3 * index + 1], ThirdValue = input2[3 * index + 2] });


var result = list1.Where(p => !list2.Any(q => q.FirstValue != p.FirstValue && q.SecondValue != p.SecondValue && q.ThirdValue != p.ThirdValue));

var sb = new StringBuilder();
result.ToList().ForEach(p => sb.AppendFormat("{0};{1};{2}\r\n", p.FirstValue, p.SecondValue, p.ThirdValue));
           
File.WriteAllText(FILEPATH, sb.ToString());

Geht sicher noch eleganter.

LaTino

"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)

A
764 Beiträge seit 2007
vor 8 Jahren

Ich habe mal rein interesse halber drei völlig unterschiedliche Ansätze erstellt:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;

namespace Coords
{
    public class Program
    {
        public static void Main(string[] args)
        {
            const string coordsString1 = "54030;720;34;64929;930;12;30000;120;3;20000;20;2;10000;10;1;";
            const string coordsString2 = "54030;720;34;64929;930;10;30000;120;3;20000;20;1;10000;10;1;";

            var unequalCoordinates1 = ObjectVariante(coordsString1, coordsString2);
            Console.WriteLine(String.Join(Environment.NewLine, unequalCoordinates1));

            var unequalCoordinates2 = StringVariante(coordsString1, coordsString2);
            Console.WriteLine(String.Join(Environment.NewLine, unequalCoordinates2));

            var unequalCoordinates3 = RegexVariante(coordsString1, coordsString2);
            Console.WriteLine(String.Join(Environment.NewLine, unequalCoordinates3));
               
            Console.ReadLine();
        }

        public class Coordinate
        {
            public int X { get; set; }
            public int Y { get; set; }
            public int Z { get; set; }

            public bool Equals(Coordinate coordinate)
            {
                return this.X == coordinate.X && this.Y == coordinate.Y && this.Z == coordinate.Z;
            }

            public override string ToString()
            {
                return String.Format("{0};{1};{2}", this.X, this.Y, this.Z);
            }
        }
        private static IEnumerable<string> ObjectVariante(string coordsString1, string coordsString2)
        {
            var coordinates1 = ParseCoordsString(coordsString1);
            var coordinates2 = ParseCoordsString(coordsString2);

            var unequalCoordinates = coordinates1.Where((t, i) => !t.Equals(coordinates2[i]));

            return unequalCoordinates.Select(c => c.ToString());
        }
        private static IList<Coordinate> ParseCoordsString(string coordsString)
        {
            var coordinates = new List<Coordinate>();
            var coordsList1 = coordsString.TrimEnd(';').Split(';').Select(c => Convert.ToInt32(c)).ToList();

            for (int i = 0; i < coordsList1.Count; i+=3)
            {
                var block = coordsList1.Skip(i).Take(3).ToList();
                var coordinate = new Coordinate() {X = block[0], Y = block[1], Z = block[2]};
                coordinates.Add(coordinate);
            }
            return coordinates;
        }

        private static IEnumerable<string> StringVariante(string coordsString1, string coordsString2)
        {
            var result = new List<string>();
            
            var e1 = coordsString1.Split(';').GetEnumerator();
            var e2 = coordsString2.Split(';').GetEnumerator();

            while (e1.MoveNext() && e2.MoveNext())
            {
                string block1 = e1.Current + ";";
                string block2 = e2.Current + ";";

                if (e1.MoveNext() && e2.MoveNext())
                {
                    block1 += e1.Current + ";";
                    block2 += e2.Current + ";";
                }
                else
                {
                    return result;
                }

                if (e1.MoveNext() && e2.MoveNext())
                {
                    block1 += e1.Current;
                    block2 += e2.Current;
                }
                else
                {
                    return result;
                }

                if (block1 != block2)
                {
                    result.Add(block1);
                }
            }

            return result;
        }

        private static IEnumerable<string> RegexVariante(string coordsString1, string coordsString2)
        {
            var list1 = GetCoordBlocks(coordsString1);
            var list2 = GetCoordBlocks(coordsString2);
            
            return list1.Except(list2);
        }

        private static IList<string> GetCoordBlocks(string coordsString1)
        {
            var result = new List<string>();

            var match = Regex.Match(coordsString1, "(?<block>[0-9]+;[0-9]+;[0-9]+);");
            while (match.Success)
            {
                result.Add(match.Groups["block"].Value);
                match = match.NextMatch();
            }

            return result;
        }
    }
}

W
955 Beiträge seit 2010
vor 8 Jahren

Geht sicher noch eleganter.

49.485 Beiträge seit 2005
vor 8 Jahren

Hallo Santana,

in zwei geschachtelten Schleifen alle Objekte mit allen vergleichen bedeutet quadratischen Aufwand. Zu den negativen Auswirkungen von quadratischem Aufwand bei größeren oder wachsenden Listen siehe Warum haben WinForms, DataGridView & LINQ eine gute Performance? [==> lineare Aufwandsklasse] ff und Mergesort langsamer als Bubblesort? [==> Nein, Messfehler / Aufwandsklasse vs. Mikrooptimierungen].

Man sollte entweder Dictionary/HashSet/Hashtable verwenden, um den Aufwand auf linear zu drücken (siehe [Artikel] Dictionary/HashSet/Hashtable: Grundlegende Informationen, Abschnitt "Hashtables zur schnellen Prüfung auf Enthaltensein") oder eine Linq-Variante verwenden, die intern die genannten Datenstrukturen verwendet.

Tut man das nicht bekommt man - möglicherweise plötzliche - Performance-Probleme, wenn die Liste im Laufe der Zeit wächst oder man es irgendwann zufällig mal mit einer viel größeren Liste zu tun hat.

BTW: Um die Objekte als Strings vergleichen zu können, reicht es nicht, wenn nirgends Leerzeichen vorkommen, sondern die Darstellung muss grundsätzlich normalisiert sein, also bei den gleichen Zahlenwerten muss sich immer der exakt gleiche String ergeben. Optionale führenden oder folgende Nullen oder unterschiedliche Repräsentationen des Wertes 0 (z.B. als 0, -0 oder <leer>) oder optionale Tausender- oder unterschiedliche Dezimaltrenner usw. darf es also alles nicht geben.

herbivore