iT邦幫忙

0

如何縮減跑迴圈的次數

假設我在View上有一個@helper可以長出我需要的樣式,傳入的變數意義如下-
options : 一個項目的選項集合(包含選項名稱Name與選項值Value)
checkResult : 一個項目的選擇結果集合(多選,只存值Value)
透過以下@helper將每一個選項options印出,然後依據選擇結果checkResult決定是否要添加屬性"checked='checked'"
但使用Array.IndexOf會造成效率過慢,希望可以透過縮減迴圈的方法,讓效率好一些,目前做法是,當已經比對過那些值,接下來在跑迴圈的時候,就不用比對剛剛比對過的值,以達到減少跑迴圈的次數,不知道是否有其他更好的方式呢?還請各位幫忙,謝謝!

@helper GetTestItemResult(dynamic[] options, dynamic checkResult) {
	for(var i = 0; i<options.Length; ++i) {
		var option = options[i];
		<input type='checkbox' 
            @Html.Raw((Array.IndexOf(checkResult.Option, option.Value)>=0)?
            "checked='checked'" : String.Empty) 
        disabled='disabled'>
		@(option.Name)
	}
}

options

Name      Value
option1     0
option2     1
option3     2
option4     3
option5     4

checkResult

Value
  0
  3
  4

目前的作法,但是是不對的結果...

@helper GetTestItemResult(dynamic[] options, dynamic checkResult) {
    var countJ=0;
	for(var i = 0; i<options.Length; ++i) {
		var option = options[i];
        for(int j=countJ; j<checkResult.Option.Count;){
            var result = checkResult.Option[j];
            if(option.Value==result){
                <input type='checkbox' checked='checked' disabled='disabled'>
                countJ=countJ+1;
                break;
            }
            else{
                <input type='checkbox' disabled='disabled'>
                break;
            }
            @(option.Name)
        }
	}
}
冰水 iT邦新手 5 級 ‧ 2019-09-19 11:57:39 檢舉
用foreach 能比較快一點
不知道這樣有沒幫助
anniecat iT邦新手 4 級 ‧ 2019-09-19 13:31:16 檢舉
Hi 冰水,foreach 的次數應該是不會比較少的,謝謝你~
3
淺水員
iT邦新手 2 級 ‧ 2019-09-19 14:02:09
最佳解答

方法一:

假設 options 跟 checkResult 都是依據 value 排序,那麼用一個變數儲存 checkResult 的「當前位置」,這樣一個迴圈就可以結束了。

方法二:

這個方法不需要排序,而是把 checkResult 轉成 hash table key=>value 那類型的資料型態,這樣查詢該 value 是否存在就很快了。

方法三:

假設不希望依據 value 排序 options ,而只對 checkResult 排序,那麼查詢 value 是否存在於 checkResult 可以用二分搜尋法,可以找看看內建函式是否有支援。


上述三種方法,程式碼大致如下 (我對C#不熟,所以用js表示)

let options=[
	{name:'option0',value:0},
	{name:'option1',value:1},
	{name:'option2',value:2},
	{name:'option3',value:3},
	{name:'option4',value:4}
];

let checkResult=[0,3,4];

function solution_1()
{
	let checkIdx=0;
	let checkVal=checkResult[checkIdx];
	for(idx in options) {
		let key=options[idx].name;
		let val=options[idx].value;
		if(checkVal === val) {
			checkVal=checkResult[++checkIdx];
			document.write(key+':checked<br>');
		} else {
			document.write(key+'<br>');
		}
	}
}

function solution_2()
{
	let valueMap={};
	checkResult.forEach(function(val){
		valueMap[val]=true;
	});
	for(idx in options) {
		let key=options[idx].name;
		let val=options[idx].value;
		if(valueMap[val]) {
			document.write(key+':checked<br>');
		} else {
			document.write(key+'<br>');
		}
	}
}

function solution_3()
{
	for(idx in options) {
		let key=options[idx].name;
		let val=options[idx].value;
		if(binarySearch(checkResult, val)!==false) {
			document.write(key+':checked<br>');
		} else {
			document.write(key+'<br>');
		}
	}
	
	//因為 js 沒有二分搜尋法,所以要自己實作
	function binarySearch(srcArray, target) {
		//程式碼:略
		//回傳找到的 index,如果找不到回傳 false
	}
}
看更多先前的回應...收起先前的回應...
anniecat iT邦新手 4 級 ‧ 2019-09-19 15:02:09 檢舉

謝謝您的幫忙~想先詢問方法一的部份,目前試起來checkIdx會有超過checkResult的索引值的問題,所以我加了一個判斷

        if(checkIdx<checkResult.Option.Count){
            result=checkResult.Option[checkIndex];
        }

,然後,方法一的checkResult應該也要先排序,對嗎?

淺水員 iT邦新手 2 級 ‧ 2019-09-19 15:47:43 檢舉
  1. 因為 js 超過索引會回傳 undefined 所以我偷懶了。C#是比較嚴格的語言,的確要處理一下。
  2. 方法一的 checkResult 要排序,而且是依據 value 排序,而不是 name。因為怕會打亂你 options 的順序,所以後面才又提出兩個方法。
anniecat iT邦新手 4 級 ‧ 2019-09-20 10:02:44 檢舉

謝謝潛水員,不知道方便再提問排序的部分嗎?
目前嘗試了以下兩個方式做排序,但都會跳出錯誤訊息,不知道還可以怎麼嘗試呢?

    //ToList()與ToArray()都嘗試過了
    var aaa = checkResult.Option;
    aaa=(
        from dynamic result in (dynamic[])aaa
        orderby result.Option ascending
        select result).ToList();

    //
    var vvv =
        from row in (dynamic[])aaa
        orderby row.Chapter ascending
        select row;

https://ithelp.ithome.com.tw/upload/images/20190920/20115336Clucweo6cg.jpg

淺水員 iT邦新手 2 級 ‧ 2019-09-20 10:34:24 檢舉
  1. C# 我真的沒在用,所以語言方面的問題我無法幫忙
  2. 我提到的方法二,就是類似其他人所說的 Dictionary(只是因為 C# 我不熟),其實我比較推薦用這個。
YoChen iT邦新手 4 級 ‧ 2019-09-20 11:03:09 檢舉

HashTable跟Dictionary不太一樣哦~XDDD
(註:C#也是有HashTable的,詳細的差異可以去Google一下)

另外,排序的部分可以調整為

var linqOptions = (from option in options
                   orderby option.Value ascending
                   select option).ToArray();

dynamic[] checkResultOption = checkResult.Option;

var linqCheckResultOption = (from val in checkResultOption
                             orderby val ascending
                             select val).ToArray();
淺水員 iT邦新手 2 級 ‧ 2019-09-20 11:24:45 檢舉

謝謝 YoChen 補充
其實我在最一開始 js 那邊用 hash table 也不是很恰當
我想表達的是 key=>value 那種資料型態
他們底層實作可能各自有不同的方式
但效能上,讀寫資料至少能到 log2(n)
我改一下上面的敘述

anniecat iT邦新手 4 級 ‧ 2019-09-20 16:26:22 檢舉

謝謝潛水員的幫忙,方法一有成功,目前嘗試往方法二試試看~
另外也謝謝謝謝YoChen的幫忙,轉型的部分還是會有不允許隱含轉換的部分,後來使用明確的轉型即可:

int?[] checkResultOption = checkResult.Option;
0
小魚
iT邦高手 1 級 ‧ 2019-09-19 11:43:48

如果多選是比較麻煩,
要不然你就先排序吧,
然後再下去找.
但是前提是你兩邊都要有排序.

anniecat iT邦新手 4 級 ‧ 2019-09-19 13:31:43 檢舉

options有排序,目前的作法有放在發問中,但是是不對的結果,在想有沒有更好的做法呢?

1
player
iT邦大師 1 級 ‧ 2019-09-19 16:06:06

如果我沒記錯的話
Array.IndexOf 應該理頭是跑回圈的
所以你的code等於是跑2層的巢式迴圈
你要不要試試看能不能改用Dictionary去做, 這樣應該會快一點

anniecat iT邦新手 4 級 ‧ 2019-09-20 10:04:06 檢舉

好的,謝謝您的建議,Dictionary的部分我再去研究看看~

1
YoChen
iT邦新手 4 級 ‧ 2019-09-19 18:32:27

如同其他大大所說,您可以參考Dictionary的作法~

@helper GetTestItemResult(dynamic[] options, dynamic checkResult)
{
    Dictionary<int, string> dictionary = new Dictionary<int, string>();

    foreach (var result in checkResult.Option)
    {
        dictionary.Add(result, "checked='checked'");
    }

    foreach (var option in options)
    {
        string checkedProperty = dictionary.ContainsKey(option.Value) ? dictionary[option.Value] : string.Empty;

        <input type='checkbox' @(checkedProperty) disabled='disabled'>
        @(option.Name)
    }
}
anniecat iT邦新手 4 級 ‧ 2019-09-20 10:04:55 檢舉

好的,謝謝YoChen~我先嘗試看看淺水員的方法一,再來研究Dictionary的作法看看~~

我要發表回答

立即登入回答