LINQ筆記20180630

LINQ筆記20180630

 

public class Person
{
	public string Name { get; set; }
	public int Age { get; set; }

	/// <summary>
	/// Equals(不一定呼叫) 與HashCode(必要呼叫)
	/// </summary>
	/// <param name="obj"></param>
	/// <returns></returns>
	public override bool Equals(object obj)
	{
		count++;
		if (obj == null || !(obj is Person))
			return false;

		var o = obj as Person;
		if (o.Name == Name && o.Age == Age)
			return true;

		return false;
	}

	/// GetHashCode相等就不會呼叫Equals
	/// 相等的時候比一次,不相等的時候比兩次
	/// <summary>
	/// GetHashCode(協助排序)
	/// </summary>
	/// <returns></returns>
	public override int GetHashCode()
	{
		hascount++;
		//// Hash區分是誰,可能是因為繼承需要區分出來(下面數字不一定需要 13 * 7 也可7 * 8)
		int hash = 13 * 7;
		if (Name != null) hash += Name.GetHashCode();
		hash += Age.GetHashCode(); return hash;
	}
}
private static int count = 0;
private static int hascount = 0;

private static void Main(string[] args)
{
	var dataArray = new int[] { 1, 2, 3, 5, 6, 8 };
	Person[] data1 =
	{
		new Person()
		{
			Name = "code6421",
			Age = 15
		},
		new Person()
		{
			Name = "mary",
			Age = 11
		}
		, new Person()
		{
			Name = "jack",
			Age = 35
		}
	};
	Person[] data2 =
	{
		new Person()
		{
			Name = "mary",
			Age = 11
		},
		new Person()
		{
			Name = "jack",
			Age = 35
		}
	};
	foreach (var item in data1.Except(data2))
	{
		if (item.Equals(item))
		{
			Console.WriteLine(item.Name);
		}
	}

	Console.WriteLine($"Equals被呼叫的次數 : {count}");
	Console.WriteLine($"GetHashCode被呼叫的次數: {hascount}");
	Console.ReadLine();
}

結果:

code6421
Equals被呼叫的次數 : 3
GetHashCode被呼叫的次數: 5

複雜型別的比較需改寫Equals()與GetHashCode()這兩個方法,若只覆寫其中一個方法會有細微的錯誤。

抽取出來

//取出來實作
public class PersonEqualComparer : IEqualityComparer<Person>
{
	public bool Equals(Person x, Person y)
	{
		if (x == null && y == null)
			return true;
		if ((x == null && y != null) ||
			(x != null && y == null))
			return false;
		if (x.Name == y.Name && x.Age == y.Age)
			return true;
		return false;
	}

	public int GetHashCode(Person obj)
	{
		int hash = 13 * 7;
		if (obj.Name != null)
			hash += obj.Name.GetHashCode();
		hash += obj.Age.GetHashCode();
		return hash;
	}
}

//需要比對時,新增物件比對
static void ExceptWithComplexType2()
{
	Person[] data1 = {
		new Person() { Name = "code6421", Age = 15 },
		new Person() { Name = "mary", Age = 11 },
		new Person() { Name = "jack", Age = 35 } };

	Person[] data2 = {
		new Person() { Name = "mary", Age = 11 },
		new Person() { Name = "jack", Age = 35 } };

	foreach (var item in data1.Except(data2, new PersonEqualComparer()))
	{
		Console.WriteLine(item.Name);
	}
}

 ## 若一天有十萬個使用者上線,則會使用NoSQL(EX:Redis)作為緩衝層,則會先從NoSQL取得所需資料,取下來後放在記憶體搜尋。

Distinct

過濾重複的資料

Concat

將資料兜起來(合併清單)

Lazy Loading

//在WHERE中還是一個迴圈
var result = data.Where(a => a > 2); //再跑
result = result.Where(a => a > 3); //先跑
foreach (var item in result)
{ 
	Console.WriteLine(item); 
}

OrderBy是少數會將集合抽取出來排序後放置暫存體

Reverse只將資料反過來(並不會抽取排序放置暫存體)

Distinct(針對一個集合產生不重複值)

Union(兩個集合的不重複值)

Zip

int[] data1 = { 1, 3, 5, 7, 9 };
int[] data2 = { 3, 7 };
foreach (var item in data1.Zip(data2, (x, y) => x + "," + y))
{
	Console.WriteLine(item);
}

印出結果:

1,3

3,7

#########################################################################################

ToDictionary()、ToLookup() =>Key值不能重複

ToDictionary()(查詢資料時,使用ToDictionary,一對一)

ToLookup()使用名子的第一個字(一對多)(類似Group)(通常取值會有兩層foreach)

ToDictionary()、ToLookup()、ToList() 立即執行

AsEnumerable() =>  (IIEnumerable<T>)只是做轉型動作

Join=>Inner Join

LINQ TO Objects(Join不會快)
NoSQL(NoJoin) =>

1.資料庫太大因資料可能重複儲存

1.Document(Json)=>Json物件全部丟入(儲存速度快)  ex: MangoDB

2.(Key-Value)=>只能用Key去查(通常用DateTime yyyy-MM-dd) ex: Redis 

3.ColumnBase =>快速取得單一資料以最少磁碟速度

Name 1,2,3,4

Age1,2,3,4

4.Cosmos(混合式資料模型)最短查詢路徑Customer  Order  Product

LeftOuterJoin(假造一個右邊給他)

private static void LeftOutJoinVer1()
{
	Department[] deps ={
		new Department() { ID = 1, Name = "Developer" },
		new Department() { ID = 2, Name = "Sales" },
		new Department() { ID = 3, Name = "Support" }
	};

	Employee[] emps = {
		new Employee() { ID = 1, Name = "code6421", Department_ID = 1 },
		new Employee() { ID = 1, Name = "tom", Department_ID = 1 },
		new Employee() { ID = 1, Name = "mary", Department_ID = 2 },
		new Employee() { ID = 1, Name = "jack", Department_ID = 2 },
		new Employee() { ID = 1, Name = "tobe", Department_ID = 4 },
	};

	var result = from s1 in emps
				 join s2 in deps on s1.Department_ID equals s2.ID into p1
				 from s3 in p1.DefaultIfEmpty(new Department() { ID = -1, Name = "(none)" })
				 select new
				 {
					 EmployeeID = s1.ID,
					 EmployeeName = s1.Name,
					 DepartmentName = s3.Name
				 };
	foreach (var item in result)
	{
		Console.WriteLine($"Name : {item.EmployeeName}, Department: {item.DepartmentName}");
	}
}

DefaultIfEmpty

var data = new Employee[] { };
//集合為空值時假造一個資料
foreach (var item in data.DefaultIfEmpty(new Employee { Name = "codeTest" }))
{
	Console.WriteLine(item.Name);
}

LINQ TO Objects => Join兩端的Key值是不定的(可用運算產生)

GroupBy(ToLookUp = Group)

Join跟Group的KEY值都是可運算的。

Department[] deps =
{
	new Department() { ID = 1, Name = "Developer" },
	new Department() { ID = 2, Name = "Sales" },
	new Department() { ID = 3, Name = "Support" }
};

Employee[] emps = 
{
	new Employee() { ID = 1, Name = "code6421", Department_ID = 1 },
	new Employee() { ID = 1, Name = "tom", Department_ID = 1 },
	new Employee() { ID = 1, Name = "mary", Department_ID = 2 },
	new Employee() { ID = 1, Name = "jack", Department_ID = 2 },
};
//兩個dataSource
var result = from root in (from s1 in emps
						   join s2 in deps on s1.Department_ID equals s2.ID
						   select new
						   {
							   DName = s2.Name,
							   EName = s1.Name
						   })
			 group root by root.DName[root.DName.Length - 1] into g2
			 orderby g2.Key
			 select new
			 {
				 Key = g2.Key,
				 Data = g2
			 };

foreach (var item in result)
{
	Console.WriteLine(item.Key);
	foreach (var sitem in item.Data)
		Console.WriteLine($"   Name : {sitem.DName}, EmpName: {sitem.EName}");
}

可改成(因Group是可以運算的)


var result = from s1 in deps
			 join s2 in emps on s1.ID equals s2.Department_ID into g
			 group s1 by new
			 {
				 Key = s1.Name[s1.Name.Length - 1],
				 DName = s1.Name,
				 Detail = g
			 } into g2
			 select new
			 {
				 Key = g2.Key.Key,
				 DName = g2.Key.DName,
				 Data = g2.Key.Detail
			 };



foreach (var item in result)
{
	Console.WriteLine(item.Key);
	foreach (var sitem in item.Data)
		Console.WriteLine($"   Name : {item.DName}, EmpName: {sitem.Name}");
}

Min、Max會出現 若資料為空時會出現例外

若需呼叫Min及Max則會跑兩次迴圈

若只想跑一次迴圈,則可使用Aggregate

但因Aggregate呼叫delegate因此效能上需要一些時間

單獨呼叫Min跟Max會較快

static void CombinMinMaxWithAggregate()
{
    int[] data = { 4, 3, 15, 7, 9 };
    //將種子帶入 x =>種子 y=>data中的參數
    var result = data.Aggregate(new  {
        Min = int.MaxValue,
        Max = int.MinValue
    },
    (x, y) => new {
        Min = Math.Min(x.Min, y),
        Max = Math.Max(x.Max, y)
    });
    Console.WriteLine($"Min : {result.Min}, Max: {result.Max}");
}

使用Aggregate

//第一種方式
var data = new string[] { "code6421", "tom", "bill" };
//code6421,tom,bill
//name是種子,Aggregate只跑一次迴圈
data.Aggregate((name, next) => name + ",");
//第二種方式
var result = string.Join(",", data);
Console.WriteLine(data.Aggregate((name, next) => name + "," + next));
Console.WriteLine(result);

Any、All(若為count確認有無資料可改為Any)

Any與Where的不同:Any找到第一筆結果就停止。 (類似FirstOrDefault但回傳值是true 跟 false)

All尋找全部都符合的資料才會回傳 true(用途不多)

安全轉型

OfType
object[] data = { 1, "12", 1L , 3};
foreach (int i in data.OfType<int>())
{
    Console.WriteLine(i);
}
Cast

強制轉型元素,失敗就拋出例外

Count、LongCount

不是IEnumerable<T>通常都是立即執行

private class Employee
{
	public int ID { get; set; }
	public int DepartmentID { get; set; }
	public string Name { get; set; }
	public int Salary { get; set; }
}

var result = Enumerable.Repeat(new Employee() { ID = 1, Name = "code" }, 10);
var p = result.FirstOrDefault();
p.Name = "tomm";
foreach (var item in result)
{
	Console.WriteLine($"ID:{item.ID} Name:{item.Name}");
}

此時會印出結果:

ID:1 Name:tomm
ID:1 Name:tomm
ID:1 Name:tomm
ID:1 Name:tomm
ID:1 Name:tomm
ID:1 Name:tomm
ID:1 Name:tomm
ID:1 Name:tomm
ID:1 Name:tomm
ID:1 Name:tomm

因為是ReferenceType

若需要取出正常資料則需在Repeat加上Select

private struct Employee1
{
	public int ID { get; set; }
	public int DepartmentID { get; set; }
	public string Name { get; set; }
	public int Salary { get; set; }
}

var result = Enumerable.Repeat(new Employee1() { ID = 1, Name = "code" }, 10);
var p = result.FirstOrDefault();
p.Name = "tomm";
foreach (var item in result)
{
	Console.WriteLine($"ID:{item.ID} Name:{item.Name}");
}
Console.ReadKey();

印出結果:

ID:1 Name:code
ID:1 Name:code
ID:1 Name:code
ID:1 Name:code
ID:1 Name:code
ID:1 Name:code
ID:1 Name:code
ID:1 Name:code
ID:1 Name:code
ID:1 Name:code

Range

解1 + 到10的數學題?

var result = Enumerable.Range(1, 10).Sum(); 

GetProperties()

var p = new Person() { Name = "code6421", Age = 12, City = "Taipei" };
var Person = p.GetType().GetProperties().Select(item => new
{
	Name = item.Name,
	Value = item.GetValue(p)
});

foreach (var item in Person)
{
	Console.WriteLine($"Property:{item.Name}\r\n Value:{ item.Value}\r\n---");
}
Console.ReadKey();
Repeat通常用來測試程試效能

Map-Reduce

Google=>BigTable

▪ C#中的Map-Reduce

▪ Map -> Select,GroupBy

▪ Reduce -> Where、Aggregate

PLINQ類似memory used

Collection.AsParallel().

 PLINQ不一定會增加你現行查詢的效能(少用,,PLINQ會運用"現行的CPU"相等的執行緒)

一般來說,當使用PLINQ時,即使元素集已經有排序過,結果也不一定是排序過的 ▪ 可以使用AsOrdered來要求PLINQ執行時,照著原來元素集的順序執行,得到與原 來元素集相同的排序結果=>速度會再慢些。

LINQ 中只要是實作IQueryable的Data Source,就會受到特別的對待。

2+3

LEFT(2) Operate(+) Right(3)

2+3+1

Node(LEFT(2) Operate(+) Right(3))

 

Expression(不停遞迴)

http://ww.api.com/query? id= 1& name=cr
隱藏實作
data.Where(a=>a.id == 1 && name == "cr");