PDA

View Full Version : سوال: تعویض Key یک Dictionary



Saeed_m_Farid
دوشنبه 05 تیر 1391, 13:10 عصر
سلام
دوستان، هر قسمتی تونستین به داد من برسین، پیشاپیش تقدیر به عمل میاد‌؛ البته خوب فقط سوال به ذهنم میاد و دنبال راه حل هستم، والا کارم گیر نیست...

یه اتفاق جالب امروز اتفاق افتاد واسه من، گفتم نظر شما رو هم بپرسم:
فرض کنید یه <Dictionary (http://msdn.microsoft.com/en-us/library/xfhwa508.aspx)<string, string داریم که توش داده هامون هستن، حالا نیاز پیدا می کنیم که مثلاً Key هایی که اولشون xxx هست بکنیم yyy! خیلی سریع می فهمیم که این‌کار ممکن نیست، چون :

Keys must be immutable as long as they are used as keys in the SortedDictionary(TKey, TValue). : 1 (http://msdn.microsoft.com/en-us/library/f7fta44c(v=vs.100).aspx)
پس مجبوریم اول آیتم های yyy‌ درست و اضافه کنیم، بعد xxx ها رو حذف؛ حالا داستان اینجاست! جناب #C شروع میکنه به جفتک اندازی، چرا؟

اول خواستم یه Extension method بنویسم، نذاشت! برای جزئیات بیشتر، این تابع من هست:
public static void RenameKey(this Dictionary<string, string> dic,
string fromKey, string toKey)
{
string value = dic[fromKey];
dic.Remove(fromKey);
dic[toKey] = value;
}

این هم الطاف #C :


Extension method must be defined in a non-generic static class
خوب من که قرار نیست واسه یه کار کوچک، کل کلاس رو static‌ کنم! ضمناً اصلاً هم تمایلی ندارم که کلاس من درآوردی واسه Dictionary بنویسم، بعدش مثلاً چنین Extension method ای توش داشته باشم، چون فردا برنامه برسه دست یکی دیگه، چه میدونه اینا چی هست؟ هرچقدر هم Document‌ کنم، کیه که داکیومنت بخونه؟ public static RenameKey<TKey, TValue>(Dictionary<TKey, TValue> dic this,
TKey fromKey, TKey toKey)
{
TValue value = dic[fromKey];
dic.Remove(fromKey);
dic[toKey] = value;
}
پس کلاً این روش رو بیخیال شدم و عطایش رو به لقایش بخشیدیم ... (مگه اینکه از دوستان کسی کمک کنه!)
بعد دیدم، بابا من تو foreach اش هم مشکل دارم! وقتی یه Key‌ عوض میکنی دیگه عملیات iteration برای اون شیء قابل اعمال نیست، یعنی درون foreach نمیشه چیزی که بعنوان iterate میدیم رو کم/زیاد کرد تغییر داد، بازهم کرامات #C:


Collection was modified; enumeration operation may not execute.>
خوب حالا باید چکار کنم؟ یه نمونه از Dictionary بسازم، اون رو تعییر بدم و در نهایت نتیجه رو به مقدار تعییر یافته انتساب بدم؛ جالب اینجاست که اینجا هم #C میره رو اعصاب! نمیشه از شیء‌ موردنظر Clone کرد؛ یعنی چیزی بنام Clone که نداریم (یا من نمیدونم!) وقتی هم یه شیء انتساب بدی، میشه یه رفرنس به اصلیه، یعنی مثل Shortcut‌! واقعاً شما باشین چکار میکنین که یه ساعت بخاطر یه کار زپرتی الّاف بشین! پس بازم مجبور شدم از روشهای از سر وا کردن استفاده کنم‌:(
Dictionary<string, string> result = new Dictionary<string, string>();
result = result.Concat(info).ToDictionary(x => x.Key, x => x.Value);

آخرش شد یه آش شله قلم کار مثل این:
public void RepairInfo(ref Dictionary<string, string> info)
{
Dictionary<string, string> result = new Dictionary<string, string>();
result = result.Concat(info).ToDictionary(x => x.Key, x => x.Value);
foreach (string item in info.Keys)
{
if (item.Contains("XXXX"))
{
string key = System.Threading.Thread.CurrentThread.CurrentCultu re.TextInfo.ToTitleCase(
item.Replace("XXXX", "YYYY").Replace("_", " "));
result.Add(key, result[item]);
result.Remove(item);
}
}
info = result;
}


هرجای کار اگه اشتباه کردم، شاگرد هر کی که یه راهنمایی یا روشنگری بنماید، خواهیم شد...
ممنون

gwbasic
دوشنبه 05 تیر 1391, 15:12 عصر
در مورد Extension Method همانطور که اشاره کردین کلاس باید static باشه دلیلشم منطقیه این متد قرار به یه کلاس یا تایپ دیگری ملحق بشه. سخت نگیرین تمام LINQ همین Extension Method ها هستن
در مورد foreach هم امکان تغییر Collection ای که روی آن پیماش صورت می گیره وجود نداره ولی شما می تونید روی Key ها به صورت زیر پیمایش انجام بدین

List<string> keys = dic.Keys.ToList();

foreach(var item in keys)
{
if (item.Contains("xxx"))
{
string value = dic[item];
dic.Remove(item);
dic[item.Replace("xxx", "yyy")] = value;
}
}

Saeed_m_Farid
دوشنبه 05 تیر 1391, 17:11 عصر
واقعاً ممنون
ولی این سوال تله بود، جناب gwbasic (http://barnamenevis.org/member.php?25140-gwbasic) افتادین توش متاسفانه! باور کنید یه سوال ساده داشتم تاپیک زدم یه ماه و خورده ای بی جواب موند، هی پست دادم کسی توجه نکرد، منم حذف‌اش کردم :(
سوال من این بود: می‌خوام در یک Node دلخواه (داخلی) در XML مقداری رو درج کنم، اگه هم وجود داشت تغییرش بدم (Linq to Xml)؛ نودهای والدش رو نمیدونم، نمی تونم یه Query درست بنویسم!
اگه لازمه تو یه تاپیک جدید جزئیاتش رو بگم یا همین جا کارهایی رو که انجام دادم، توضیح بدم...

tooraj_azizi_1035
دوشنبه 05 تیر 1391, 19:17 عصر
فرض کنید یه <Dictionary (http://msdn.microsoft.com/en-us/library/xfhwa508.aspx)<string, string داریم که توش داده هامون هستن، حالا نیاز پیدا می کنیم که مثلاً Key هایی که اولشون xxx هست بکنیم yyy! خیلی سریع می فهمیم که این‌کار ممکن نیست، سلام

var matches= from kvp in dictionary
where kvp.ToString().StartsWith("xxx")
select kvp.Key.ToString().Replace("xxx","yyy")



foreach(var kvp in matches)
{
dictionary.Add(kvp.Key, kvp.Value);
dictionary.Remove(kvp.Key));

}



دوست من کد رو تست نکردم ولی روش کار درسته ممکنه ایراد هم داشته باشه چون اینجا من VS ندارم تست کنم.

Saeed_m_Farid
دوشنبه 05 تیر 1391, 20:50 عصر
دوست من کد رو تست نکردم ولی روش کار درسته ممکنه ایراد هم داشته باشه چون اینجا من VS ندارم تست کنم.
سلام و ممنون
ولی متاسفانه منطق کد ایراد داره نه سینتکس، میدونین چرا؟ چون نوع ضمنی (implicit type) کوئری که شما نوشتین string هست! و هیچ ردّپایی از ارتباطش با KeyValuePair های قبلی باقی نمی مونه، بعبارت دیگه تو قسمت foreach معلوم نیست جای ??? چی باید گذاشت:

foreach (var kvp in matches)
{
info.Add(kvp, ???);
info.Remove(???);
}

چون برخلاف نام متغیر (kvp فکر کنم منظور خلاصه KeyValuePair باشه)، نوع ضمنی string هست، درست نمیگم؟
===================
فرضاً ما مشکل رو با کد زیر حل کردیم:
var matches =
from kvp in info
where kvp.Key.ToString().StartsWith("xxx")
select new { Key = kvp.Key.ToString().Replace("xxx", "yyy"), Value = kvp.Value} ;

foreach (var kvp in matches)
{
info.Add(kvp.Key, kvp.Value);
info.Remove(???);
}

(میدونم حل نشد گفتم، فرضاً!) بازم مشکل اولیه رو که شما حل نکردین! باز هم : "امکان تغییر Collection ای که روی آن پیماش صورت می گیره وجود نداره" یعنی دقیقاً اون کاری که تو foreach شما انجام میشه ...
بازم از همفکری شما خیلی تشکر میکنم.

gwbasic
دوشنبه 05 تیر 1391, 22:39 عصر
سعید جان با همین جزئیات که توضیح دادی سوالتو در مورد node‌ مطرح کن در یک تاپیک مستقل تا ببینیم مسئله چیه؟ اگه سورس کد هم برای فرضیات باشه که چه بهتره

Saeed_m_Farid
سه شنبه 06 تیر 1391, 10:42 صبح
سعید جان با همین جزئیات که توضیح دادی سوالتو در مورد node‌ مطرح کن در یک تاپیک مستقل تا ببینیم مسئله چیه؟ اگه سورس کد هم برای فرضیات باشه که چه بهتره
اطاعت امر شد: افزودن یک Node در انتهای عنصر موردنظر XML (مربوط به Linq to XML) (http://barnamenevis.org/showthread.php?347921-%D8%A7%D9%81%D8%B2%D9%88%D8%AF%D9%86-%DB%8C%DA%A9-Node-%D8%AF%D8%B1-%D8%A7%D9%86%D8%AA%D9%87%D8%A7%DB%8C-%D8%B9%D9%86%D8%B5%D8%B1-%D9%85%D9%88%D8%B1%D8%AF%D9%86%D8%B8%D8%B1-XML-%28%D9%85%D8%B1%D8%A8%D9%88%D8%B7-%D8%A8%D9%87-Linq-to-XML%29)