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");