Problem z rozpoczęciem programu

0

Cześć,
mam pewien problem ze zinterpretowaniem założenia do zadania.
Treść zadania:

W pliku tekstowym znajdują się dane o zakupach klientów w postaci:

id_klienta; nazwisko i imię; nazwa_towaru;cena;zakupiona_ilość

Identyfikator klienta ma postać
cNNNNN
gdzie N cyfra ze zbioru [0...9]
np.
c00001;Kowalski Jan;bułka;2;100

Wczytać dane z pliku i wypisać na konsoli w kolejnych wierszach:

poprzedzone napisem "Nazwiska" dane posortowane wg nazwisk w porządku rosnącym (porządek rekordów z tymi samymi nazwiskami jest określany przez identyfikatory klientów - rosnąco),
poprzedzone napisem "Koszty" dane posortowane wg kosztów zakupów w porządku malejącym (porządek rekordów z tymi samymi kosztami jest określany przez identyfikatory klientów - rosnąco) z dodatkowym dopiskiem na końcu w nawiasach:  koszty:  kosztZakupu (np. (koszt: 200.0)),
poprzedzone napisem "Klient 1" dane o wszystkich zakupach  klienta  o identyfikatorze "c00001" (w odrębnych wierszach)
poprzedzone napisem "Klient 2" - w odrębnych wierszach -dane o wszystkich zakupach  klienta  o identyfikatorze "c00002"  (w odrebnych wierszach) (a więc uwaga: w pliku muszą być klienci o identyfikatorach c00001 i c00002)

Np. dla pliku w postaci:
c00004;Nowak Anna;banany;4;50
c00003;Kowalski Jan;mleko;4;5
c00001;Kowalski Jan;mleko;4;10
c00001;Kowalski Jan;mleko;5;2
c00002;Malina Jan;mleko;4;2
c00002;Malina Jan;chleb;3;5
c00001;Kowalski Jan;bulka;2;100

postać wyniku na konsoli winna być nastepująca:

Nazwiska
c00001;Kowalski Jan;mleko;4.0;10.0
c00001;Kowalski Jan;mleko;5.0;2.0
c00001;Kowalski Jan;bulka;2.0;100.0
c00003;Kowalski Jan;mleko;4.0;5.0
c00002;Malina Jan;mleko;4.0;2.0
c00002;Malina Jan;chleb;3.0;5.0
c00004;Nowak Anna;banany;4.0;50.0

Koszty
c00001;Kowalski Jan;bulka;2.0;100.0 (koszt: 200.0)
c00004;Nowak Anna;banany;4.0;50.0 (koszt: 200.0)
c00001;Kowalski Jan;mleko;4.0;10.0 (koszt: 40.0)
c00003;Kowalski Jan;mleko;4.0;5.0 (koszt: 20.0)
c00002;Malina Jan;chleb;3.0;5.0 (koszt: 15.0)
c00001;Kowalski Jan;mleko;5.0;2.0 (koszt: 10.0)
c00002;Malina Jan;mleko;4.0;2.0 (koszt: 8.0)

Klient 1
c00001;Kowalski Jan;mleko;4.0;10.0
c00001;Kowalski Jan;mleko;5.0;2.0
c00001;Kowalski Jan;bulka;2.0;100.0

Klient 2
c00002;Malina Jan;mleko;4.0;2.0
c00002;Malina Jan;chleb;3.0;5.0

Uwaga: programy nie dające pokazanej formy wydruku otrzymują 0 punktów.

Niezbędne jest stworzenie klasy, opisującej zakupy klientów i operowanie na jej obiektach. Nie przyjmuję rozwiązań działających na "surowych" Stringach.
Wyszukiwanie zakupów klienta po jego identyfikatorze ma być zrealizowane jako metoda z argumentem = identyfikator klienta. Metoda ta ma działać dla dowolnych klientów i zapewniać b. szybkie, efektywne wyszukiwanie (podpowiedź - użyć mapy)..

Mój problem polega na tym że nie za bardzo rozumiem jaką mam stworzyć strukturę danych. Ja bym sobie utworzył klasę z klientami i klasę z zakupami a potem powiązał bym jedno z drugim za pomocą mapy. Ale wydaje mi się że w zadaniu chodzi o inne zestawienie danych. Wydaje mi się bez sensu trzymanie np wszystkich danych w jednej klasie. A później mieć mapę której kluczem był by nr id klienta a wartościami klasa.

Co wy na to?

0

Problem jak to wykonać już rozwiązałem, teraz mam problem z sortowaniem.
Mam takie klasy:

 
    static class Towar {

        String nazwaTowaru;
        int cena;

        public Towar(String nazwaTowaru, int cena) {
            this.nazwaTowaru = nazwaTowaru;
            this.cena = cena;
        }
    }

    static class Klient {

        String idKlienta, nazwiskoImie;

        public Klient(String idKlienta, String nazwiskoImie) {
            this.idKlienta = idKlienta;
            this.nazwiskoImie = nazwiskoImie;
        }
    }

    static class Zamowienie {

        Klient k;
        Towar t;
        int ilosc;

        public Zamowienie(Klient k, Towar t, int ilosc) {
            this.k = k;
            this.t = t;
            this.ilosc = ilosc;
        }
    }

i taką mapę, oczywiście wypełnioną odpowiednio:

        Map<String, List<Zamowienie>> transakcje = new HashMap<String, List<Zamowienie>>();

i jak teraz posortować tą mapę wg nazwisk. Wiem, że będę musiał utworzyć dodatkową Listę i ją sortował ale nie wiem jaki comparator mam utworzyć.

0

i jak ? udalo się? daj znac bo tez nad tym glowkuje...

0

Cześć!
Czy ktoś może pomóc z tym zadaniem? Jak powinna wyglądać metoda wczytująca i modyfikująca te dane?

0

Jeśli chodzi o wczytywanie to polecam użyć klasy Scanner do wczytywania linii. Linie, jak widać, składają się ze składowych złączonych znakiem ;, a więc można użyć albo metody split z klasy String, albo robić nowe Scannery dla każdej linii i każdemu ustawiać setDelimiter(";"), a potem korzystać z next(), nextInt() itd. Scanner powinien być wygodniejszy niż metoda split + ręczne konwersje.

Jeśli chodzi o sortowanie to polecam użyć metody sort z klasy Collections + zrobić kilka implementacji Comparatorów.

0

Twój problem polega na tym, że musisz zejść z piątej postaci normalnej na ziemię. :)
Tłumacząc po ludzku musisz rozbić każdą obecną w mapie parę <String, List<Zamowienie>> na serię par:
<String1, Zamówienie1>
//...
<String1, ZamówienieN>
//...
<StringN, Zamówienie1>
//...
<StringN, ZamówienieN>
Dopiero wtedy masz możliwość posortować "mapę". Natomiast prościej by było gdybyś mógł się pozbyć tych Stringów (bez aluzji :)).

Mając listę kluczy do mapy możesz z mapy wyciągnąć zamówienia:

List<String> indeksy = keyList(transakcje, new LexicOrder<>());

Do tego potrzeba metody takiej jak ta:

static <K, V> List<K> keyList(Map<K, V> map, Comparator<? super K> c)
{
	final List<K> keys = new ArrayList<>(map.size());
	for(K element: map.keySet())
		Lists.sortingInsert(element, keys, false, c);
	return keys;
}

Potem rozbicie postaci normalnej:

List<Pair<String, Zamówienie>> zamówienia = new ArrayList<>();
for(String indeks: indeksy)
	zamówienia.add(new Pair<>(indeks, transakcje.get(indeks)));

I na koniec posortowanie:

List<Pair<String, Zamówienie>> zamówieniaPosortowane = Lists.sortedBy(zamówienia,
new Comparator<>()
{
	@Override public int compare(Pair<String, Zamówienie> p1, Pair<String, Zamówienie> p2)
	{
		return String.CASE_INSENSITIVE_ORDER
			.compare(p1.getValue().k.nazwiskoImie, p2.getValue().k.nazwiskoImie);
	}
}

Nie jest to maksymalnie efektywne ponieważ ostatnie sortowanie nie będzie całkowicie po polsku - a w przypadku nazwisk dobrze by było, żeby taki "Ącki" poszedł na początku, a nie na końcu. Żeby to poprawić należałoby dziedziczyć po klasie Pair<String, Zamówienie>, przesłonić sobie toString tak aby do Stringa wyrzucało zawartość getValue().k.nazwiskoImie, a następnie użyć komparatora LexicOrder - który potrafi sobie sortować wg alfabetów narodowych i dodatkowo cache'ować klucze (CollationKey) porównywania elementów. Ewentualnie trzeba stworzyć interfejs, który z dowolnego typu E wyciągnie sobie String (czyli getValue().k.nazwiskoImie) i podać go jako dodatkowy parametr konstruktora do zmodyfikowanego potomka LexicOrder.

W przypadku map trzeba jeszcze uważać aby kluczami nie zostały tablice lub wyliczenia bo standardowa HashMap nie potrafi liczyć haszy dla tych typów i wali bezwartościową wersję z Object.

Do tego powyżej potrzebny jest uniwersalny typ pary, podstawowe komparatory i podstawy sortowania zwykłych list:

  1. Pair:
package com.olamagato.util;
import com.olamagato.util.Arr;
import java.util.Objects;

/**
 * Reprezentuje skojarzoną parę klucz-wartość będącą
 * odwzorowaniem niezmiennym.
 * @param <K> typ klucza
 * @param <V> typ wartości
 * @author Olamagato
 * @version 1.0
 */
public class Pair<K, V>
{
	/**
	 * Tworzy odwzorowanie pary klucza i wartości.
	 * @param key klucz odwzorowania
	 * @param value wartość odwzorowania
	 */
	public Pair(K key, V value)
	{
		this.key = key;
		this.value = value;
	}

	/**
	 * Zwraca klucz odwzorowania.
	 * @return klucz
	 */
	public K getKey() { return key; }

	/**
	 * Zwraca wartość odwzorowania.
	 * @return wartość
	 */
	public V getValue() { return value; }

	/**
	 * Zwraca wartość równości między dwoma obiektami odwzorowań
	 * @param oth obiekt innej pary
	 * @return true jeżeli ten obiekt oraz oth są tożsame
	 */
	@Override public boolean equals(Object obj)
	{
		if(obj == null || this.getClass() != obj.getClass())
			return false;
		else if(obj == this)
			return true;
		@SuppressWarnings(value = "unchecked")
		final Pair<K, V> other = (Pair<K, V>)obj;
		return Objects.deepEquals(this.key, other.key) &&
			Objects.deepEquals(this.value, other.value);
	}

	/**
	 * Kod haszujący odwzorowania zależy od kodu klucza i wartości
	 * odwzorowania.
	 * @return kod haszujący odwzorowania
	 */
	@Override public int hashCode()
		{ return Arrays.hashCode(keyHashCode(), value); }

	/**
	 * Zwraca cache'owany kod haszujący klucza odwzorowania.
	 * @return kod haszujący klucza
	 */
	public int keyHashCode()
	{
		return keyHashCodeCache != 0 ? keyHashCodeCache
			: (keyHashCodeCache = Arrays.hashCode(key));
	}

	private K key;
	private V value;
	private int keyHashCodeCache; //cache kodu haszującego klucza
} //public static final class Pair<K, V>
  1. I cała reszta w jednej klasie narzędziowej:
package com.olamagato.util;
import java.text.CollationKey;
import java.text.Collator;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;

/**
 * Klasa pomocnicza do obsługi list.
 * @author Olamagato
 * @version 1.4
 */
abstract public class Lists
{
	/**
	 * Klasa obiektu naturalnie porównującego obiekty typu E.
	 * Wszędzie gdzie potrzeba komparatora dla typu elementów mających swój
	 * naturalny porządek, tam można umieścić komparator w takiej postaci
	 * {@code new NaturalOrder<E>()}, gdzie E jest klasą posiadającą swój
	 * naturalny porządek wyrażony implementacją interfejsu
	 * {@literal Comparable<E>}. Klasy używające: SortedList.
	 * @param <E> elementy listy mające swój naturalny porządek
	 */
	public static class NaturalOrder<E extends Comparable<? super E>>
		implements Comparator<E>
	{
		/**
		 * Komparator zwracający wynik porównania elementów mających swój
		 * naturalny porządek.
		 * @param e1 pierwszy element
		 * @param e2 drugi element
		 * @return liczba dodatnia gdy e1>e2, zero gdy e1==e2 lub ujemna
		 * gdy e1< e2
		 */
		@Override public int compare(E e1, E e2) { return e1.compareTo(e2); }
	} //public static class NaturalOrder<E extends Comparable<? super E>>

	/**
	 * Klasa obiektu porównującego leksykograficznie obiekty typu E. Wszędzie
	 * gdzie potrzeba komparatora dla typu elementów mających swój porządek
	 * leksykograficzny można umieścić komparator w takiej postaci
	 * {@code new LexicOrder<E>()}, gdzie E jest klasą posiadającą jakiś
	 * porządek leksykograficzny obiektów w postaci tekstowej. Domyślnie
	 * porównywanie następuje wg porównania leksykograficznego dla domyślnego
	 * alfabetu domyślnego języka w bieżącej JVM. Komparator porównuje elementy
	 * na podstawie przeprowadzonej ekstrakcji obiektów CollationKey dla postaci
	 * tekstowej porównywanych elementów. Klucze porównywania dla poszczególnych
	 * elementów są przy użyciu generowane i zachowywane do późniejszego użycia.
	 * Pozwala to na efektywne sortowanie lub inne operacje wymagające wielu
	 * porównań tych samych elementów. Domyślny sposób użycia:
	 * {@code new LexicOrder<>()} Zobacz:
	 * {@link SortedList},  {@link Lists}{@literal sortedBy()}
	 * @param <E> typ elementów listy
	 */
	public class LexicOrder<E> implements Comparator<E>
	{
		/**
		 * Tworzy obiekt porównujący postaci tekstowe elementów E w bieżącej
		 * lokalizacji z domyślnym poziomem dekompozycji i siły porównywania.
		 */
		public LexicOrder()
		{
			this.collator = Collator.getInstance();
		}
		/**
		 * Tworzy obiekt porównujący postaci tekstowe elementów E w podanej
		 * lokalizacji, z domyślnym dla niej poziomem dekompozycji i siły
		 * porównywania.
		 * @param locale żądana lokalizacja
		 */
		public LexicOrder(Locale locale)
		{
			this.collator = Collator.getInstance(locale);
		}
		/**
		 * Tworzy obiekt porównujący postaci tekstowe elementów E w podanej
		 * lokalizacji, z podanym poziomem dekompozycji i siły porównywania.
		 * @param locale żądana lokalizacja
		 * @param decomposition żądany poziom dekompozycji
		 * @param strength żądana dokładność porównywania
		 */
		public LexicOrder(Locale locale, int decomposition, int strength)
		{
			this.collator = Collator.getInstance(locale);
			this.collator.setDecomposition(decomposition);
			this.collator.setStrength(strength);
		}
		/**
		 * Porównuje wartości tekstowe elementów e1 i e2 przy pomocy
		 * wygenerowanego obiektu typu Collator oraz tworzonych z opóźnieniem
		 * kluczy porównywania umieszczanych w mapie, która pozwala odzyskać te
		 * klucze przy ponownym użyciu.
		 * @param e1 pierwszy element
		 * @param e2 drugi element
		 * @return wynik porównania postaci tekstowych tych elementów
		 */
		@Override public int compare(E e1, E e2)
		{
			CollationKey ck1 = cache.get(e1), ck2 = cache.get(e2);
			if(ck1 == null)
				cache.put(e1, ck1 = collator.getCollationKey(e1.toString()));
			if(ck2 == null)
				cache.put(e2, ck2 = collator.getCollationKey(e2.toString()));
			return ck1.compareTo(ck2);
		}
		private final HashMap<E, CollationKey> cache = new HashMap<>();
		private final Collator collator;
	} //public static class LexicOrder<E> extends ArrayList<E>

	/**
	 * Tworzy listę elementów E o zawartości identycznej jak podawana lista,
	 * lecz posortowaną wg klucza podawanego przez comparator.
	 * @param <E> typ elementów
	 * @param list lista elementów
	 * @param comp comparator dwóch elementów typu E
	 * @return lista elementów posortowanych wg podanego indeksera
	 */
	public static <E> List<E> sortedBy(List<E> list, final Comparator<E> comp)
	{
		final List<E> result = new ArrayList<>(list.size());
		for(E element: list) //wstawia również elementy nieunikalne
			Lists.sortingInsert(element, result, false, comp);
		return result;
	}

	/**
	 * Przeszukuje za pomocą podziałów połówkowych całą posortowaną rosnąco
	 * listę o dostępie swobodnym i zwraca indeks znalezionego elementu toFind
	 * lub wartość -ins - 1, gdzie ins jest pozycją wstawienia elementu, czyli
	 * większy od pierwszego mniejszego elementu na liście. Metoda może być
	 * wykorzystana do szybkiego określenia występowania elementu toFind na
	 * liście list, czyli efektywnie zastąpić metodę contains().
	 * @param <E> typ elementu listy
	 * @param list posortowana rosnąco lista elementów
	 * @param toFind element, którego indeks jest do znalezienia
	 * @param c obiekt komparatora porównującego dwa elementy listy
	 * @return nieujemny indeks znalezionego elementu lub -punkt_wstawienia-1
	 */
	public static <E>
		int binarySearch(E toFind, List<E> list, Comparator<? super E> c)
	{
		int l = 0;
		int r = list.size() - 1;
		for(int cmp, mid = (l + r) >>> 1; l <= r; mid = (l + r) >>> 1)
		{
			cmp = c.compare(toFind, list.get(mid));
			if(cmp > 0) l = ++mid;
			else if(cmp < 0) r = --mid;
			else return mid; //toFind found
		}
		return -(l + 1); //toFind not found.
	}

	/**
	 * Wstawia element toInsert do podanej posortowanej listy w taki sposób,
	 * aby znalazł się on między elementem mniejszym i większym. Elementy są
	 * porównywane za pomocą podanego komparatora. Parametr allowReplace
	 * pozwala na zastępowanie równych elementów już występujących na liście,
	 * czyli wymusza unikalność wartości wstawianych elementów.
	 * Pomyślne wstawienie/zastąpienie zwraca true, a brak wykonanej akcji
	 * zwraca false. Metoda może być efektywnie wykorzystana do szybkiego
	 * tworzenia posortowanych list.
	 * @param <E> typ elementu
	 * @param toInsert element do wstawienia na listę list
	 * @param list posortowana lista (może być pusta)
	 * @param comparator obiekt porównujący elementy
	 * @param allowReplace pozwala na zastępowanie elementów istniejących
	 * @return true w przypadku wstawienia unikalnego
	 */
	public static <E> boolean sortingInsert(E toInsert, List<E> list,
		boolean allowReplace, Comparator<? super E> comparator)
	{
		int found = binarySearch(toInsert, list, comparator);
		if(found < 0)
			list.add(-found - 1, toInsert);
		else if(allowReplace)
			list.set(found, toInsert); //dozwolone zastępowania
		else //dostawia element równy przed istniejący
		{
			list.add(found, toInsert);
			return false;
		}
		return true;
	}

	private Lists() { }
}

Trochę tego dużo, ale wszystko pochodzi z działającego kodu, więc nie będę ciął, żeby czegoś nie pogubić.
Javadoc powinien być jasny, więc nie będę się dodatkowo produkował.

ps. Ups. Chyba to już po ptakach. Nie rzuciłem okiem na daty :)

0

Dzięki za pomoc!

1 użytkowników online, w tym zalogowanych: 0, gości: 1