Tallinna likool
Informaatika Instituut











LINQ andmehaldus

Jaagup Kippar






Tallinn 2011

Sisukord
Alustus	2
Esimesed ksklused	2
lesandeid	4
Hulgatehted	5
lesandeid	5
Avaldistega ksud	5
lesandeid	8
LINQ ja tekstifailid	8
lesandeid	9
Funktsioonid	9
lesandeid	10
Klassid	10
lesandeid	12
Grupeerimine	13
Viiteid	13


Alustus


Language INtegrated Query nime all on .NET raamistikku ning sealtkaudu ka C# keelde kaasa tulnud meeldiv ja kasulik komplekt andmetega mber kimise vahendeid. Esimese nhtava abilisena neist on lisandunud massiividele hulk ksklusi andmete lihtsamaks ttlemiseks. Lhikesed ksud, mis siiani olid nt. Ruby ja Pythoni-nimeliste keelte prusmaa leiavad selle kaudu tee ka C# juurde. Teine hea omadus uuel tehnoloogial on, et sarnaselt on vimalik andmeid ksida vga erinevate andmeallikate juurest - massiivist andmebaasini. Juurde on loodud lihtsalt liides nimega IEnumerable mis thistab igasugust jrjestatavat samatbilist andmehulka ning LINQ-ga seotud kske saab rakendada kigile selle liidese reegleid jrgivatele andmetele. 

Esimesed ksklused

Et massiivid vimalik teha vikesed ja kergesti ligipsetavad, siis alustame tutvustusniteid sealt.

Nitetulemuste mugavamaks vljatrkiks on kasutatud string-klassi Join-ksklust, mis loetelu elemendid heks tekstiks hendab. Nii et koodiligu


int[] m = {2, 6, 4, 5, 3, 2};
Console.WriteLine(string.Join(" ", m));

tulemuseks ekraanil on rida
			
2 6 4 5 3 2

Erinevate vrtuste kuvamiseks on ksklus Distinct

            IEnumerable<int> erinevad = m.Distinct();

mis siis tulemusena annab IEnumerable tpi muutujasse saadud erinevad tulemused. Tahtes loetelu tagasi massiiviks saada, aitab ksklus ToArray. Siis vimalik elementide poole taas otse jrjekorranumbri kaudu prduda. 

            int[] m2 = erinevad.ToArray<int>();

Trkkides uus massiiv vlja on nha, et algsete andmetega vrreldes on sealt korduv 2 linud kaduma. 
			
2 6 4 5 3

Kik see on vimalik ka he ksuga kirja panna

            int[] m3 = m.Distinct().ToArray();


Esimene nitlik koodifail tervikuna.

using System;
using System.Collections.Generic;
using System.Linq;

class Massiivitehted {
    public static void Main(string[] arg) {
            int[] m = {2, 6, 4, 5, 3, 2};
            Console.WriteLine(string.Join(" ", m));
            IEnumerable<int> erinevad = m.Distinct();
            Console.WriteLine(string.Join(" ", erinevad));
            int[] m2 = erinevad.ToArray<int>();
            Console.WriteLine(string.Join(" ", m2));
            int[] m3 = m.Distinct().ToArray();
            Console.WriteLine(string.Join(" ", m3));
    }
}

/*
2 6 4 5 3 2
2 6 4 5 3
2 6 4 5 3
2 6 4 5 3
*/


Jrgnevalt veel mned kasulikud ksklused. Elementide arvu loendis annab Count

        Console.WriteLine("Elementide arv: " + m.Count());
		
Tagurpidi jrjekorras tulevad vrtused ksuga Reverse.
		
        Console.WriteLine("Tagurpidi: " + string.Join(" ", m.Reverse()));

Loetelu algusest soovitud arvu vrtuste vlja vtmiseks sobib Take

        Console.WriteLine("Neli esimest: " + string.Join(" ", m.Take(4)));
		
Tahtes nende kskude abil lpust soovitud kogust elemente vtta, on heks vimaluseks kske kombineerida. Kigepealt prata rida mber, siis vtta kolm esimest ning tulemus taas mber prata. Et kik need ksud on seotud IEnumerable liidesega, siis saab eelmise ksu vljundi ilusti uue ksu sisendiks lugeda.
		
        Console.WriteLine("Kolm viimast: " + string.Join(" ", 
              m.Reverse().Take(3).Reverse()));


Olemas ka traditsioonilised andmekogumitega seotud ksklused, mida vimalik sarnaselt kivitada. Suurima, vhima, summa ja keskmise leidmiseks lihtsalt vastavad inglisekeelsed snad juhul, kui kogumist on vimalik soovitut vlja arvutada.
			  
        Console.WriteLine("Suurim: " + m.Max());
        Console.WriteLine("Vhim: " + m.Min());
        Console.WriteLine("Summa: " + m.Sum());
        Console.WriteLine("Keskmine: " + m.Average());

Sobiva vrtuse kindlaks tegemiseks ksklus Contains

        Console.WriteLine("Sisaldab 3: " + m.Contains(3));

Ning nite kood tervikuna:
		
			  
using System;
using System.Collections.Generic;
using System.Linq;

class Massiivitehted2{
    public static void Main(string[] arg) {
        int[] m = { 2, 6, 4, 5, 3, 2 };
        Console.WriteLine("Algne: " + string.Join(" ", m));
        Console.WriteLine("Elementide arv: " + m.Count());
        Console.WriteLine("Tagurpidi: " + string.Join(" ", m.Reverse()));
        Console.WriteLine("Neli esimest: " + string.Join(" ", m.Take(4)));
        Console.WriteLine("Kolm viimast: " + string.Join(" ", 
              m.Reverse().Take(3).Reverse()));

        Console.WriteLine("Suurim: " + m.Max());
        Console.WriteLine("Vhim: " + m.Min());
        Console.WriteLine("Summa: " + m.Sum());
        Console.WriteLine("Keskmine: " + m.Average());

        Console.WriteLine("Sisaldab 3: " + m.Contains(3));
    }
}

/*
Algne: 2 6 4 5 3 2
Elementide arv: 6
Tagurpidi: 2 3 5 4 6 2
Neli esimest: 2 6 4 5
Kolm viimast: 5 3 2
Suurim: 6
Vhim: 2
Summa: 22
Keskmine: 3,66666666666667
Sisaldab 3: True
*/


lesandeid

* Loo massiiv kmnekonna tisarvuga
* Trki vlja neist suurim
* Trki vlja nelja esimese arvu summa
* Eralda neli esimest arvu uude massiivi
* Trki loodud massiiv vlja tagurpidises jrjekorras

Hulgatehted

Kahe kogumi vrdlemiseks on ka mugavad ksud olemas. Union liidab kokku kahe kogumi vrtused eemaldades korduvad elemendid. Intersect leiab vrtused mis mlemis olemas on. Except annab hulkade vahe, ehk nitab tulemusena vaid neid, mis esimeses hulgas on, aga teises ei ole. Kahe kogumi vrtuste lihtsaks kokkuliitmiseks sobib Concat.

using System;
using System.Collections.Generic;
using System.Linq;

class Massiivitehted3
{
    public static void Main(string[] arg)
    {
        string[] jukukeeled = { "eesti", "vene" };
        string[] katikeeled = { "eesti", "soome", "inglise" };
        Console.WriteLine("Kahe peale oskavad: " + 
             string.Join(" ",jukukeeled.Union(katikeeled)));
        Console.WriteLine("Mlemad oskavad (hisosa): " +
             string.Join(" ", jukukeeled.Intersect(katikeeled)));
        Console.WriteLine("Ainult Juku oskab: " +
             string.Join(" ", jukukeeled.Except(katikeeled)));
        Console.WriteLine("Kik oskused loetelus: " +
             string.Join(" ", jukukeeled.Concat(katikeeled)));

    }
}

/*
Kahe peale oskavad: eesti vene soome inglise
Molemad oskavad (hisosa): eesti
Ainult Juku oskab: vene
Koik oskused loetelus: eesti vene eesti soome inglise
*/

lesandeid

* Loo kolm massiivi tisarvudega, mis thistavad vastavas peatuses peatuvate bussiliinide numbreid
* Leia, kas leidub mni bussiliin, mis peatuks kigis kolmes peatuses
* Leia kik bussiliinid, millega ei saa sita esimesest peatusest teise
* Vljasta kik erinevad liininumbrid


Avaldistega ksud

Sobivate andmete leidmiseks saab kasutada funktsioonide parameetrisse edastatavaid lambda-avaldisi. Massiivi

        int[] m = { 2, 6, 4, 5, 3, 2 };
		
neljast suuremate vrtuste leidmiseks sobib ksklus 	

        Console.WriteLine("Neljast suuremaid: " + m.Count(a => a > 4));
		
mis annab vastuseks

        Neljast suuremaid: 2

Andmeid le kontrollides paistab see ige olevat - neljast suuremad vrtused on 5 ja 6.

Avaldise a => a > 4 vasakul pool (enne => mrki) on lihtsalt tht a. Siinsete kskude puhul thendab see, et kogumi iga liikme puhul pannakse ta kigepealt muutujasse nimega a ning seejrel tehakse temaga toimetus, mis kirjas avaldise paremal pool - kus vib vastavat muutujat kasutada. Count-funktsioon loendab kokku vaid need elemendid, kus avaldise puhul vljastatakse tene tulemus, st. millise konkreetse elemendi puhul avaldis a>4 on tene. 

Kui tahta tingimusele vastavaid elemente nha ka, siis, selleks sobib ksklus Where. Tulemusena tagastatakse 6 ja 5.

       Console.WriteLine("Neljast suuremad: "+
             string.Join(" ", m.Where(a => a > 4)));
 

Kui korraga vaja vaid hte tingimusele vastavat vrtust, siis selle vljastab ksklus First.
 
        Console.WriteLine("Esimene neist: " + m.First(a => a > 4));

Kogu massiivitit andmeid mne tehte abil muuta lubab Select. Siin nites jvad tisarvud tisarvudeks, kuid vajadusel saab .NET neljandast versioonist alates vljastada tulemuse ka muu vajaliku andmetbina. Niteks on nnda vimalik he ksuga muuta terve massiivitis arve tekstideks vi vastupidi.
		
        Console.WriteLine("Kahega korrutatult: " +
             string.Join(" ", m.Select(a => a * 2)));


Ka elementide arv ei pea massiivi lbiva ksu puhul samaks jma. SelectMany arvestab, et igast elemendist tekib vastusena loend ning kikide nende loendite elementide vrtused korjatakse lpuks hte suurde jadasse kokku. Siin nites siis luuakse igast ettevetavast arvust kaheelemendiline massiiv, kus esimeseks elemendiks on ettevetud arv ise ning teiseks tema ruut. 			 
			 
a => new int[]{a, a * a}

SelectMany korjab nd igast m-nimelises massiivi elemendist tekkinud arvupaarid kokku ning annab vlja pika arvude ning ruutude jada.
			 
	    Console.WriteLine("Arvud ja ruudud: " +
             string.Join(" ", m.SelectMany(a => new int[]{a, a * a})));


Nnda siis massiiv 
			 
int[] m = { 2, 6, 4, 5, 3, 2 };

annab eelpooltoodud SelectMany ning avaldise tulemusena vlja jada

Arvud ja ruudud: 2 4 6 36 4 16 5 25 3 9 2 4


Loendist vimalik ka muul moel vrtusi ktte saada. Algusest kuni soovitud elemendini massiivis vljastab ahela ksklus TakeWhile - algusest vetakse vrtusi senikaua kuni avaldises olev tingimus pole tidetud. Ehk siis massiivist

        int[] m = { 2, 6, 4, 5, 3, 2 };

avaldis

			 Console.WriteLine("Kuni vrtuseni 5: " +
             string.Join(" ", m.TakeWhile(a => a != 5)));

annab tulemuseks
			 
Kuni vrtuseni 5: 2 6 4


Soovitud kohast edasi liikuda on vimalik ksuga SkipWhile			 

        Console.WriteLine("Alates vrtusest 5: " +
             string.Join(" ", m.SkipWhile(a => a != 5)));
			 
annab sama massiivi puhul siis tulemuseks

Alates vrtusest 5: 5 3 2
			 
			 
using System;
using System.Collections.Generic;
using System.Linq;

class Massiivitehted4{
    public static void Main(string[] arg) {
        int[] m = { 2, 6, 4, 5, 3, 2 };
        Console.WriteLine("Neljast suuremaid: " + m.Count(a => a > 4));
        Console.WriteLine("Neljast suuremad: "+
             string.Join(" ", m.Where(a => a > 4)));
        Console.WriteLine("Esimene neist: " + m.First(a => a > 4));

        Console.WriteLine("Kahega korrutatult: " +
             string.Join(" ", m.Select(a => a * 2)));
	    Console.WriteLine("Arvud ja ruudud: " +
             string.Join(" ", m.SelectMany(a => new int[]{a, a * a})));

			 Console.WriteLine("Kuni vrtuseni 5: " +
             string.Join(" ", m.TakeWhile(a => a != 5)));
        Console.WriteLine("Alates vrtusest 5: " +
             string.Join(" ", m.SkipWhile(a => a != 5)));
        Console.WriteLine("Kahanevalt sorditult "+ 
               string.Join(" ",m.OrderBy(a => -a)));
    }   
}

/*
Neljast suuremaid: 2
Neljast suuremad: 6 5
Esimene neist: 6
Kahega korrutatult: 4 12 8 10 6 4
Arvud ja ruudud: 2 4 6 36 4 16 5 25 3 9 2 4
Kuni vrtuseni 5: 2 6 4
Alates vrtusest 5: 5 3 2
Kahanevalt sorditult 6 5 4 3 2 2
*/

lesandeid

* Loo massiiv inimeste pikkustega
* Loe kokku, mitu neist on pikemad kui 180 cm.
* Loo uus massiiv, kus inimesed on pikkuste jrjekorras
* Trki pikkused kuni esimese 180-cm pikkuse tegelaseni
* Trki pikkused prast esimest 180-cm pikkust tegelast
* Loo massiiv, kus kirjas on pikkused meetrites (nt. 1.65).
* Loo massiiv, kus pikkused kirjas tekstina (nt. 1m 65cm).

LINQ ja tekstifailid

Tekstifaili mugavaks lugemiseks ja kirjutamiseks on klassi System.IO.File juurde loodud mugavad ksklused ReadAllLines ning WriteAllLines. Nnda saab kergesti lugeda failitis teksti ridade kaupa massiivi vi siis jllegi kirjutada sealt faili. Nitena loetakse mllu failis ujujad.txt olevad nimed. Nendest vetakse maha failis jooksjad.txt olevad nimed ning tulemus kirjutatakse faili ainultujujad.txt.

using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;

class Failivordlus
{
    public static void Main(string[] arg)
    {
        File.WriteAllLines("ainultujujad.txt",
          File.ReadAllLines("ujujad.txt").Except(File.ReadAllLines("jooksjad.txt"))
        );
    }
}
/*
>type ujujad.txt
Kati
Siiri
Madis
Mart

>type jooksjad.txt
Juku
Kati
Mati

>type ainultujujad.txt
Siiri
Madis
Mart
 
>c:\WINDOWS\Microsoft.NET\Framework\v4.0.30319\csc.exe Failivordlus.cs
*/


Sarnane toimetusnide ka arvudega. Sisendiks olevas tekstifailis on viie suusahppekohtuniku antud punktid vistlejale. Vastavalt reeglitele eemaldatakse sealt suurim ja vhim vrtus ning lejnud kolmest arvutatakse keskmine. Selleks siis loetakse failis olevad read massiivi, Select ksu ja double.Parse abil muudetakse arvudeks. Jrjestatakse arvu enese vrtuse jrgi. Esimene jetakse vlja ning sealt alates jetakse alles kolm elementi, mis jrjestatuse tttu on siis allesjjad. Ning Average arvutab neist keskmise. (16,3+16,2+14,3)=15,6 - sama nitab ka programmi t tulemus.


using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;

class Failipunktid
{
    public static void Main(string[] arg)
    {
          Console.WriteLine(File.ReadAllLines("punktid.txt").Select(p => double.Parse(p))
		      .OrderBy(p => p).Skip(1).Take(3).Average());
    }
}
/*
E:\jaagup\11\09\linq>type punktid.txt
16,3
16,2
17,4
12,5
14,3

E:\jaagup\11\09\linq>Failipunktid
15,6
*/

lesandeid

* Loo fail inimeste pikkustega
* Leia suurim pikkus
* Leia kik keskmisest viksemad pikkused
* Leia mediaan, st pikkus, millest on pooled suuremad ja pooled viksemad
* Paiguta pikkused sordituna teise faili
* Trki teise faili kigepealt paarisarvulised ning siis paarituarvulised pikkused.


Funktsioonid

Eelnevalt toodud nidetes pandi avaldiste juures tehtavad kontrollid ja tehted otse sulgude sisse nagu niteks m.Count(a => a > 4). Sellist kirjapilti nimetatkse lambda-avaldiseks. Iseenesest on see lhendkuju olukorrast, kus ksklusele Count antakse ette funktsioon, mis saab parameetriks muutuja nimega a. Ning funktsioon vljastab avaldise a>4  vrtuse. Pikemate arvutuste korral aga ongi mistlik arvutused pris eraldi funktsiooni paigutada. Siin on eraldi pisikese nitena toodud stringi pikkuse vljastav funktsioon pikkus. 

    public static int pikkus(string tekst){
      return tekst.Length;
    }


Hiljem kivitamiseks piisab funktsiooni nimi lihtsalt avaldise sulgudesse panna.

	  IEnumerable<int> pikkused=eesnimed.Select(pikkus);

Tulemusega saab ringi kia nagu tavalise IEnumerable-tpi andmestikuga.

C# juures on ka mugav foreach-nimeline ksklus kogumite andmete lbi kimiseks. Pole vaja teada elementide jrjekorranumbreid, lihtsalt kshaaval vetakse kogumi liikmed ette ning nendega on ploki sees vimalik sooritada soovitud toiming.

        foreach(String element in vastus){
          Console.WriteLine(element);
        } 

Nite kood tervikuna:

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

class Funktsioonid{
    public static string Sorenda(string tekst) {
      return string.Join(" ", tekst.ToCharArray());
    }
    public static int pikkus(string tekst){
      return tekst.Length;
    }
    public static void Main(string[] arg) {
        string[] eesnimed = { "Juku", "Kati", "Katrin", "Madis" };
	  IEnumerable<int> pikkused=eesnimed.Select(pikkus);
	  Console.WriteLine(String.Join(" ", pikkused));
        IEnumerable<string> vastus=eesnimed.Select(Sorenda);
        foreach(String element in vastus){
          Console.WriteLine(element);
        } 
    }
}

/*
E:\jaagup\11\09\linq>Funktsioonid
4 4 6 5
J u k u
K a t i
K a t r i n
M a d i s
*/

lesandeid
* Loo massiivi inimeste enimedega
* Koosta funktsioon toimetuseks, kus nime eestht on viidud lppu. 
* Ttle nnda kik nimed Select-ksu abil.
* Koosta funktsioon nimede thtede thekoodide kokkuliitmiseks.
* Leia nnda igale massiivis olevale snale vastav rsikood.
* Koosta funktsioon, kus etteantud sna phjal luuakse massiiv, kus esimeseks liikmeks on sna esimene tht, teiseks kaks esimest jne. kuni viimase liikme juures on tegemist terve snaga. Kogu SelectMany abil nnda kikidest algse massiivi snalikudest tekkivad jupid kokku hte suurde massiivi.

Klassid
Objektorienteeritud programmeerimises grupeeritakse andmed ja funktsioonid klassidesse. Ning LINQ vimaldab ka sellisel kujul andmestikega mugavalt toimetada. 

Siin nitena vetud lihtsalt ks kahemtmelise tasandi punkt oma x ja y koordinaadiga. Juures funktsioonid vrtuste ksimiseks kshaaval ning terviktekstina.
    class TasandiPunkt {
        int x, y;
        public TasandiPunkt(int ux, int uy) {
            x = ux; y = uy;
        }
        public int KysiX() { return x; }
        public int KysiY() { return y; }
        public override String ToString() { return "(" + x + " " + y + ")"; }
    }

Kik kolmest suurema x-iga punktid saab Where-kskluse abil ktte jrgnevalt:
            IEnumerable<TasandiPunkt> vastus2 =  punktid.Where(p => p.KysiX()>3);

Sama tulemuse annab SQL-i laadse LINQ-sntaksiga pring:

            IEnumerable<TasandiPunkt> vastus = 
			     from p in punktid where p.KysiX() > 3 select p;

Pikema pringu puhul on lhenemised sarnasemadki. Jrgnevas nites ksitakse kolmest suuremate x-idega punktide y-koordinaadid.

            Console.WriteLine(string.Join(" ", 
			     from p in punktid where p.KysiX() > 3 select p.KysiY()));

            Console.WriteLine(string.Join(" ", 
			     punktid.Where(p => p.KysiX()>3).Select(p=>p.KysiY())));


Liides IEnumerable on mnes mttes LINQ-toimingute aluseks. See on kljes massiivil, listil ning mnel muulgi andmekogumit sisaldaval klassil. Vajadusel aga saab seda tpi vljundit ka ise luua. Lisand yield returni juures annab teada, et funktsiooni t jooksul yield-i abil antud vrtused kogutakse kokku ning tagastatakse tervikuna IEnumerable-tbile vastava komplektina. Siin nites luuakse nnda klassi eksemplarile ette antud koguses arvude ruute. 

    class RuutudeLooja {
        int kogus;
        public RuutudeLooja(int uusKogus) {
            kogus = uusKogus;
        }
        public IEnumerable<int> Ruudud() {
            for (int i = 1; i <= kogus; i++) {
                yield return i * i;
            }
        }
    }

T tulemusena neb siis mitmel moel ksitud punktide andmeid. Samuti arvude ruute filtreerituna etteantud tingimuse jrgi.

using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;


namespace linq2 {
    class TasandiPunkt {
        int x, y;
        public TasandiPunkt(int ux, int uy) {
            x = ux; y = uy;
        }
        public int KysiX() { return x; }
        public int KysiY() { return y; }
        public override String ToString() { return "(" + x + " " + y + ")"; }
    }
    class RuutudeLooja {
        int kogus;
        public RuutudeLooja(int uusKogus) {
            kogus = uusKogus;
        }
        public IEnumerable<int> Ruudud() {
            for (int i = 1; i <= kogus; i++) {
                yield return i * i;
            }
        }
    }
    class Klassid {
        public static void Main(string[] arg) {
            TasandiPunkt[] punktid = { 
                new TasandiPunkt(3, 5),
                new TasandiPunkt(4, 6) 
             };
            IEnumerable<TasandiPunkt> vastus = 
			     from p in punktid where p.KysiX() > 3 select p;
            Console.WriteLine(string.Join(" ", vastus));
            IEnumerable<TasandiPunkt> vastus2 =  punktid.Where(p => p.KysiX()>3);
            Console.WriteLine(string.Join(" ", vastus2));
            Console.WriteLine(string.Join(" ", 
			     from p in punktid where p.KysiX() > 3 select p.KysiY()));
            Console.WriteLine(string.Join(" ", 
			     punktid.Where(p => p.KysiX()>3).Select(p=>p.KysiY())));

		    RuutudeLooja r = new RuutudeLooja(5);
            Console.WriteLine(string.Join(" ", r.Ruudud().Where(arv => arv > 10)));
        }
    }
}
/*
E:\jaagup\11\09\linq>Klassid
(4 6)
(4 6)
6
6
16 25
*/

lesandeid
* Loo klass mille vljadeks inimese eesnimi ja pikkus. Lisa konstruktor andmete sisestamiseks ning meetodid vrtuste ksimiseks.
* Koosta inimestest massiiv.
* Trki vlja kikide eesnimed. Kasuta tulemuse saamiseks nii valemi (Select(...)) kui lause (from ...) kuju.
* Nita ainult nende eesnimesid, kelle pikkus on viksem kui 180.
* Jrjesta inimesed nende pikkuste jrgi.

* Loo klass, mille eksemplar suudab genereerida soovitud koguse juhuarve etteantud vahemikus IEnumerable kujul.
* Meetodi parameetrina on vimalik mrata, kas arvude jaotus on htlane vi on keskmisi vrtusi rohkem.
Grupeerimine
Grupeerimise tulemusena saab koondada sarnaste tunnustega andmed grupiti kokku - olgu siis inimesed nende elukohalinna vi arvud mingite tunnuste alusel. Edasi vimalik siis juba gruppe lhemalt vaadelda - olgu elementide arvu loendada, keskmisi vtta vi muul moel. Andmete loomiseks kasutame siin Enumerable.Range ksklust, saab hekorraga valmis mitukmmend arvu.
Nitena jaotame nad gruppidesse vastavalt sellele, milline jk kolmega jagamisel igast arvust jb. 

	 var grupid=from arv in arvud group arv by arv%3;

Tekkinud grupid saab mugavasti lbi kia foreach-tskliga. Lihtsamal juhul ei pea me muutujatpide peale mtlema ning vime tbiks mrata var - selle peale otsib kompilaator ise sobiva tbi andmetega ringi kimiseks. Grupi puhul on heks vljaks Key, mis nitab tunnuse vrtust, mille jrgi grupeeriti. Grupp ise on IEnumerable tpi andmestik, millest niteks Joini abil vin vrtused kergesti tekstina vlja trkkida. Selle poole vimalik samuti foreach-tskli abil prduda vi siis ToArray kskluse abil tulemus massiivi vlja vtta nagu hiljem tehtud. Siin aga trkitakse lihtsalt vlja, millised vrtused iga vtme alla jid. 

	 foreach(var grupp in grupid){
	   Console.WriteLine("Jk "+grupp.Key+" arvud "+String.Join(" ",grupp));
	 }

Toimuva le rohkem kontrolli hoidmiseks on vimalik andmetbid ka selgemalt mrata. Tp IGrouping sisaldab endas andmete vtmeid ja vrtusi. Praegusel juhul IGrouping<int, int> thendab, et vti (ehk jk jagamisel kolmega) on tbist int ning vtmele vastava vrtustejada elemendid samuti tbist int. Kui niteks vtmeks oleks pikkus sentimeetrites ning vrtustejadaks selle pikkusega inimesed, siis oleks tbiks IGrouping<int, string>. GroupBy annab tulemusena kigepealt selliste IGrouping-tpi vtmele vastavate vrtusteahelate IEnumerable-tpi loetelu.
 	 IEnumerable<IGrouping<int, int>> grupid2=arvud.GroupBy(arv => arv % 3);

Edasi on loetelust vimalik ksikud elemendid tskli (vi niteks Where-ksu) abil vlja vtta ning nende andmeid pruukima hakata. Juures ka nide, kuidas vtmele vastavad vrtused massiivina ktte saada ning neist esimene vtta. 

	 foreach(IGrouping<int, int> grupp in grupid2){
	   Console.WriteLine("Jk "+grupp.Key+"  arvud "+String.Join(" ",grupp));	   
	   int[] grupiarvud=grupp.ToArray<int>();
	   Console.WriteLine("Esimene: "+grupiarvud[0]);
	 }

Nite kood tervikuna:

using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
public class Grupid{
  public static void Main(string[] arg){
     IEnumerable<int> arvud=Enumerable.Range(101, 20); //alates 101st 20 arvu
	 Console.WriteLine(String.Join(" ", arvud));
	 var grupid=from arv in arvud group arv by arv%3;
	 foreach(var grupp in grupid){
	   Console.WriteLine("Jk "+grupp.Key+" arvud "+String.Join(" ",grupp));
	 }
	 
	 IEnumerable<IGrouping<int, int>> grupid2=arvud.GroupBy(arv => arv % 3);
	 foreach(IGrouping<int, int> grupp in grupid2){
	   Console.WriteLine("Jk "+grupp.Key+"  arvud "+String.Join(" ",grupp));	   
	   int[] grupiarvud=grupp.ToArray<int>();
	   Console.WriteLine("Esimene: "+grupiarvud[0]);
	 }
  }
}
/*
E:\jaagup\11\09\linq>Grupid
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
Jk 2 arvud 101 104 107 110 113 116 119
Jk 0 arvud 102 105 108 111 114 117 120
Jk 1 arvud 103 106 109 112 115 118
Jk 2  arvud 101 104 107 110 113 116 119
Esimene: 101
Jk 0  arvud 102 105 108 111 114 117 120
Esimene: 102
Jk 1  arvud 103 106 109 112 115 118
Esimene: 103
*/

lesandeid
* Koosta eesnimede loetelu
* Grupeeri nimed pikkuse jrgi
* Nita tulemused vlja (pikkus, nimed ...)
* Nita, mitu nime iga pikkusega on
* Nita igast paarisarvulise nimepikkusega grupist vlja esimene nimi.

Anonmne tp
Soovides andmed he programmiosa juures kokku grupeerida, pole selleks vaja alati eraldi klassi vi struktuuri looma hakata . Ksklus new lubab looksulgude vahel andmetbi lennult tekitada ning muutujatp var suudab selle kinni pda. Nites tehakse objekt p vljadega x ja y. Ning vljatrkil saab x-i vrtuse tiesti ktte.

using System;
public class AnonyymneTyyp{
  public static void Main(string[] arg){
    var p=new{x=3, y=5};
    Console.WriteLine(p.x);
  }
}
/*
E:\jaagup\11\09\linq>AnonyymneTyyp.exe
3
*/

Sellise tbi saab mugavalt ra kasutada niteks tekstifailist andmeid lugedes. Phjalikumal ja viisakamal juhul tasuks lapse tarvis teha omaette klass ning temast eksemplar luua ning sinna sisse andmeid panna ja ksida. Kui aga ainult korraks vaja andmeid mllu lugeda ja tdelda, siis saab ka lihtsamalt. 
Jrgnevas nites vtab klassi File ksklus ReadAllLines failist nimepikkused.txt vlja kik seealsed read. Fail ise jrmine

nimepikkused.txt
Juku 173
Mari 165
Kati 170
Madis 173
Mati 173
Aadu 175
Anu 165

Kuna massiiv vastab IEnumerable reeglitele, siis saab selle panna ka LINQ-pringu sisse. Et andmeid oleks kergem hiljem tdelda jagame rea thiku kohalt kaheks ning moodustame uue objekti. Rea esimesest poolest saab vli nimega eesnimi, teine pool muudetakse int.Parse-ksklusega tisarvuks ning lheb vastava tbiga vljaks pikkus. Substring aitab teksti tkeldada, IndexOf(" ") teatab thiku asukoha. Automaatselt tekkis tp millel vljad eesnimi ja pikkus ning neid on vimalik edaspidiste arvutuste juures kasutada - praegu siis vljatrkiks.


using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.IO;
public class Grupifail{
  public static void Main(string[] argumendid){
     var lapsed=from rida in File.ReadAllLines("nimedpikkused.txt") 
	  select new{
	       eesnimi=rida.Substring(0, rida.IndexOf(" ")), 
	       pikkus=int.Parse(rida.Substring(rida.IndexOf(" ")))
	  };
     foreach(var laps in lapsed){
	     Console.WriteLine(laps.eesnimi+" pikkusega "+laps.pikkus);
     }
  }
}
/*
E:\jaagup\11\09\linq>Grupifail
Juku pikkusega 173
Mari pikkusega 165
Kati pikkusega 170
Madis pikkusega 173
Mati pikkusega 173
Aadu pikkusega 175
Anu pikkusega 165
*/


Sama andmestiku saab mnevrra mugavamalt sisse lugeda avaldiste keelt kasutades. File.ReadAllLines sobib taas andmemassiivi allikaks. Reast kahe vrtuse ktte saamiseks on mugav see kigepealt thiku koha pealt massiiviks tkeldada ning siis edasi kummastki massiivielemendist objektivli teha. lal nites tulnuks selle tarbeks uus lause kirjutada, siin aga saab avaldisi ritta sttides elda, et kigepealt tuleb hes Select-is rida Split-i abil thiku koha pealt tkeldada. Tulemusena on igale reale vastavas elemendis ks kaheelemendiline massiiv. Jrgmise Selecti abil saab luua uue objekti mrates massiivi 0-indeksiga liikme eesnimeks ning jrgmise prast tisarvuks muutmist perekonnanimeks.

     var lapsed=File.ReadAllLines("nimedpikkused.txt").Select(rida => rida.Split(' ')).
	      Select(m => new{eesnimi=m[0], pikkus=int.Parse(m[1])});

Edasi vib objektikogumiga kituda nagu mujalgi, niteks jrjestades nad pikkuse jrjekorda.

     var lapsedPikkusteJarjekorras=lapsed.OrderBy(laps => laps.pikkus);

Tskli abil saab need sealt ka ilusti ktte.

	 foreach(var laps in lapsedPikkusteJarjekorras){
	     Console.WriteLine(laps.eesnimi+" pikkusega "+laps.pikkus);
	 }

Ka faili kirjutamine lheb llatavalt valutult. File.WriteAllLines soovib andmete kirjutamiseks stringide kogumit. Select-ksu abil vormistame iga lapse objektist teksti mille algul on lapse pikkus, jrgnevad thik ja eesnimi.

	 File.WriteAllLines("sortpikkused.txt", 
	     lapsedPikkusteJarjekorras.Select(laps => laps.pikkus+" "+laps.eesnimi));

Ka grupeerimise kannatab suuremate muredeta ette vtta. Laste andmete kmnendite kaupa grupeerimiseks saab vtmeks tisosa pikkuse suhtest kmnega (st. 167 puhul nt. 16). Mis omakorda kmnega korrutatud, et prisandmeid mugavam lugeda oleks. 

Edasi saab gruppidega toimetada juba sarnaselt kui eelmises nites.  Laste nimede stringimassiivina vljavtmise juures aitab samuti Select eesnimede eristamiseks ning ToArray massiiviks muutmiseks (kui seda peaks parajasti vaja minema). Tsklimuutuja kymnend vastab ise IEnumerable reeglitele ning Count() annab niteks elementide arvu. Laste andmete sobival kujul tekstiks vormistamist toetab taas Select. 

	 var lapsedPikkusKymnenditena=lapsed.GroupBy(laps => laps.pikkus/10*10);
	 foreach(var kymnend in lapsedPikkusKymnenditena){
	    string[] eesnimed=kymnend.Select(laps => laps.eesnimi).ToArray<string>();
	    Console.WriteLine(kymnend.Key+", "+kymnend.Count()+" last: "+
		   String.Join(", ", kymnend.Select(laps => laps.eesnimi+"-"+laps.pikkus)));
	 }

Nide tervikuna:

using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.IO;
public class Grupifail2{
  public static void Main(string[] argumendid){
     var lapsed=File.ReadAllLines("nimedpikkused.txt").Select(rida => rida.Split(' ')).
	      Select(m => new{eesnimi=m[0], pikkus=int.Parse(m[1])});
     var lapsedPikkusteJarjekorras=lapsed.OrderBy(laps => laps.pikkus);
	 foreach(var laps in lapsedPikkusteJarjekorras){
	     Console.WriteLine(laps.eesnimi+" pikkusega "+laps.pikkus);
	 }
	 File.WriteAllLines("sortpikkused.txt", 
	     lapsedPikkusteJarjekorras.Select(laps => laps.pikkus+" "+laps.eesnimi));
	 var lapsedPikkusKymnenditena=lapsed.GroupBy(laps => laps.pikkus/10*10);
	 foreach(var kymnend in lapsedPikkusKymnenditena){
	    string[] eesnimed=kymnend.Select(laps => laps.eesnimi).ToArray<string>();
	    Console.WriteLine(kymnend.Key+", "+kymnend.Count()+" last: "+
		   String.Join(", ", kymnend.Select(laps => laps.eesnimi+"-"+laps.pikkus)));
	 }
	 
  }
}
/*
E:\jaagup\11\09\linq>Grupifail2
Mari pikkusega 165
Anu pikkusega 165
Kati pikkusega 170
Juku pikkusega 173
Madis pikkusega 173
Mati pikkusega 173
Aadu pikkusega 175
170, 5 last: Juku-173, Kati-170, Madis-173, Mati-173, Aadu-175
160, 2 last: Mari-165, Anu-165

E:\jaagup\11\09\linq>type nimedpikkused.txt
Juku 173
Mari 165
Kati 170
Madis 173
Mati 173
Aadu 175
Anu 165

E:\jaagup\11\09\linq>type sortpikkused.txt
165 Mari
165 Anu
170 Kati
173 Juku
173 Madis
173 Mati
175 Aadu
*/

lesandeid
* Koosta tekstifail, kus hes tulbas kirjas inimese eesnimi ning teises linn, kus ta sndinud
* Loe andmed objektidena mllu
* Vljasta inimeste eesnimed
* Sorteeri andmed linnanimede jrjekorras, vljasta teise faili
* Vljasta erinevad linnade nimed
* Vljasta iga linna kohta, mitu inimest selles sndinud on
* Vljasta ka igas linnas sndinud inimeste nimed

Laiendusmeetodid
Koos LINQ tulekuga ilmus C#-keelde tore tiendus - ksklusi on vimalik lisada ka klassidele, millest me otse prida ei saa vi ei taha. Nndamoodi on massiividele ja muudele IEnumerable-liidesele vastavatele tpidele lisatud hulk kasulikke ksklusi. Sarnaselt saab aga oma rakenduse kontekstis muudki tpi objektide oskusi tiendada. 

Meetodeid saab juurde lisada staatilise klassi abil, mille staatilise meetodi esimesele parameetrile on ette lisatud vtmesna this. Kui vastav nimeruum hiljem using-ksuga klge vetakse, siis selle usingu skoobis kitub lisatud meetod nagu oleks ta vastava klassi isendimeetod. Ehk siis nitena siin luuakse nimeruumis LaiendusMeetodid staatilise klassi sisse ksklus Pikkus, mis saab parameetriks stringi.

fail: LaiendusMeetodid.cs
using System;
namespace LaiendusMeetodid{
  public static class TekstiMeetodid{
     public static int Pikkus(this String s){
	    return s.Length;
	 } 
  }
}

Kui jrgnevas failis eelnev nimeruum klge vetakse, siis on kikidel stringidel automaatselt kljes ksklus nimega Pikkus().

fail: LaiendusProov.cs
using System;
using LaiendusMeetodid;
public class LaiendusProov{
  public static void Main(string[] arg){
    Console.WriteLine("Tere".Pikkus());
  }
}
/*
E:\jaagup\11\09\linq>LaiendusProov
4
*/

Omaprane, et ei loe, millise nimega klassi sisse vastav laiendusmeetod pandud on. Kiusatusena tekkis mte kontrollida, mis siis saab, kui samanimeline laiendusmeetod on defineeritud mitme sama nimeruumi klassi sees. Tulemuseks oli kompilaatoripoolne veateade, mis andis mrku, et enam pole selge, millist ksklust kivitada tahetakse.

Juurde nitena mned laiendusmeetodid, millest ehk rohkem abi viks olla. ArvudeSumma paneb tekstile klge kskluse, mis jagab teksti snadeks ning liidab kokku need, mis arvud on. 

Ka tavalisele int-tbile saab ksklusi juurde lisada. Siin nites astendamiskskluse.

Kolmanda funktsioonina on toodud nide massiivile oskuste lisamiseks. Ksklus thjenda muudab etteantud tisarvulise massiivi kik vrtused nullideks.

fail: LaiendusMeetodid2.cs
using System;
namespace LaiendusMeetodid{
   static class TekstiMeetodid2{
      public static int ArvudeSumma(this String s){
	     string[] m=s.Split(' ');
		 int summa=0;
		 int abi=0;
		 foreach(string sona in m){
		   if(int.TryParse(sona, out abi)){
		     summa+=abi;
		   }
		 }
		 return summa;
	  }
   }
   static class ArvuMeetodid{
     public static int astmes(this int arv, int aste){
	    int abi=1;
		for(int i=1; i<=aste; i++){
		   abi*=arv;
		}
		return abi;
	 }
	 public static void Tyhjenda(this int[] m){
	    for(int i=0; i<m.Length; i++){
		  m[i]=0;
		}
	 }
   }
}

Kivitamisel vib laiendusmeetodeid klge vtta mitmest failist, peaasi, et nimeruum klapib. Siingi nites on Pikkus()-nimeline meetod prit failist LaiendusMeetodid.cs, muud lisandused aga failist LaiendusMeetodid2.cs. Nii tuleb hoolitseda, et kompilaatorile kik vajalikud tkid kttesaadavad oleksid - olgu siis ksureal failinimesid ette andes vi arenduskeskkonnas projekti juurde kuuluvaid faile mrates.

fail: LaiendusProov2.cs
using System;
using LaiendusMeetodid;
public class LaiendusProov2{
  public static void Main(string[] arg){
    Console.WriteLine("Tere".Pikkus());
	Console.WriteLine("5 pirni ja 12 una".ArvudeSumma());
	Console.WriteLine(3.astmes(2));
	int[] arvud={2,4, 5};
	arvud.Tyhjenda();
	Console.WriteLine(String.Join(" ", arvud));
  }
}
/*
E:\jaagup\11\09\linq>csc LaiendusMeetodid.cs LaiendusMeetodid2.cs LaiendusProov2.cs
Microsoft (R) Visual C# 2010 Compiler version 4.0.30319.1
Copyright (C) Microsoft Corporation. All rights reserved.

E:\jaagup\11\09\linq>LaiendusProov2
4
17
9
0 0 0
*/

lesandeid
* Loo stringile laiendusmeetod esithe vljastamiseks
* Loo stringile laiendusmeetod, mis vljastaks seal sees olnud arvud massiivina
* Loe arvumassiivile laiendusmeetod, mille abil on vimalik seal sees olnud arve nihutada
   etteantud sammu jagu
* Loo stringile laiendusmeetodid samanimelise faili sisu lugemiseks vi kirjutamiseks.
* Koosta/leia omaloodud klass. Lisa sellele oskusi laiendusmeetodi abil.

ldisi lesandeid
* Tutvu nimeruumi System.IO klasside Directory ning File oskustega failinimede ja sisude kuvamisel
* Nita vlja kik kataloogis olevad failinimed
* Nita vlja nimed koos pikkustega
* Sorteeri failinimed pikkuste jrjekorras
* Nita kigi tekstifailide pikkused
* Nita tekstifailide pikkuste summa
* Nita iga tekstifaili kohta seal olevate ridade arv
* Nita iga tekstifaili kohta seal olevate snade arv
* Liida kokku kik kataloogi tekstifailides olevate arvude vrtused
* Koosta rekursiivne alamprogramm leidmaks kigi tekstifailides olevate arvude summa etteantud kataloogis ning selle alamkataloogides. Katseta tulemust.

Viiteid

Heiki Thise loodud piobjekt
http://www.e-ope.ee/_download/euni_repository/file/669/LINQ.zip/LINQ/index.html

Eestikeelsest ppematerjalist leiab vrt selgitusi LINQ ja XMLi koos kasutamise kohta. Samuti tutvutakse paindliku var-muutujatbi ning kigu pealt loodavate anonmsete objektidega.

Microsofti ametlik tutvustusleht
http://msdn.microsoft.com/en-us/library/bb308959.aspx

Ametlikul lehel on heade selgitavate nidetega lbi kidud pea kik LINQ philised ksud, mis andmekogumina otse rakendada saab, samuti tehtud nendest hsti loetav kokkuvte.

