2015-02-19 3 views
0

У меня есть список загрузок из XML.Сравнение списков различного типа с linq

XML-выглядит следующим образом:

<TablesToSynchronize> 
<Table name="dbo.Table1" /> 
<Table name="dbo.Table2" /> 
<Table name="dbo.Table3" /> 
<Table name="dbo.Table4" /> 
</TablesToSynchronize> 

Я хочу, чтобы сравнить список XElement со списком string, но я не могу получить его вместе.

private static readonly XDocument TableSettings = XDocument.Load(GetAssemblyDirectory() + @"\Tables.xml"); 
    private static List<XElement> TablesToSync = new List<XElement>(); 

    static void Main(string[] args) 
    { 
     { 
      var test = TableSettings.Descendants("Table").Select(x => x.Attribute("name").Value.ToString()); 

      test = test.Where(args.ToList().Contains).ToList(); 


      var test2 = (from x in TableSettings.Descendants("Table") 
       where x.Attribute("name").Value == "dbo.Table1" 
       select x).ToList(); 

      //TablesToSync = ?? 
     } 
    } 

вар test2 работает нормально, но он возвращает только XElement с «Table1». Я хочу скомпоновать args и проверить, соответствует ли какой-либо элемент XElement атрибуту ("name").Value.

Что мне здесь не хватает?

арг содержит dbo.Table1, dbo.Table2, dbo.Table3

UPDATE: test2 дает мне список XElement содержащий dbo.Table1. test дает мне пустой список string.

Выход должен быть List<XElement>

+0

извините, это должно быть dbo.Table везде. Будет обновлен – MrProgram

+0

, вы можете использовать [предложение соединения] (https://msdn.microsoft.com/en-us//library/bb311040.aspx?f=255&MSPPError=-2147217396) нечто вроде 'from x в TableSettings.Descendants (" Таблица ") из y в args join x.Attribute (" name "). Значение равно y select x' – Grundy

+0

возможно, это будет работать test = test.where (t => args.ToList(). Содержит (t)) ToList() – Pumpkin

ответ

1

Ниже будет возвращать два элемента:

args = new[] { "dbo.Table1", "dbo.Table3" }; 
var test = (from elt in TableSettings.Descendants("Table") 
      where args.Contains(elt.Attribute("name").Value) 
      select elt).ToList(); 
Debug.Assert(test.Count==2); 

или

args = new[] { "dbo.Table1", "dbo.Table3" }; 
var test = (from elt in TableSettings.Descendants("Table") 
      where args.Contains((string)elt.Attribute("name")) 
      select elt).ToList(); 

или эквивалент в качестве способа цепи:

var test = TableSettings.Descendants("Table") 
         .Where(elt => args.Contains(elt.Attribute("name").Value)) 
         .ToList(); 

XAttribute явно конвертируется в одну из основных .NET, поэтому вам нужно использовать листинг (string). В противном случае .NET будет сравнивать экземпляр XAttribute с строковым значением.

Если у вас есть много аргументов и беспокоиться, что Contains медленно, вы можете преобразовать массив строк в словарь:

var argsDict = args.ToDictionary(x => x); 
var test = TableSettings.Descendants("Table").      
       Where(elt => argsDict.ContainsKey(elt.Attribute("name").Value)) 
       .ToList(); 

Быстрая проверка 10000000 итераций возвращает эти не-очень-значимые результаты:

  • Где: 33466320 клещи
  • Регистрация: 32560386 тиков
  • Словарь: 22998512 тиков
  • HashSet: 25979814 клещи

Разница между Where и Join пренебрежимо мала (~ 2%) и может быть легко отнести к шуму. Словарь или hashset, хотя и значительно быстрее (~ 30%).

На самом деле различия между эквивалентными методами сильно колеблются и практически бессмысленны.

Это неудивительно, поскольку словарь/hashset, по сути, действует, поскольку индекс будет действовать в SQL. Фактически SQL Server будет использовать хэш-соединения, если одна из двух таблиц в соединении (относительно) мала.

Словарь может работать лучше, если вам нужно получить доступ больше, чем ключ от аргументов, так как вы можете искать и извлекать соответствующее значение за одну операцию. Это похоже на использование покрывающего индекса в SQL:

MyOtherClass v=null; 
var test = from elt in TableSettings.Descendants("Table") 
      let value = elt.Attribute("name").Value 
      where argsDict.TryGetValue(value, out v) 
      select new {elt, v}; 

Это довольно некрасиво, хотя в C# 5. Это где (ныне слом) декларации выражения в C# 6 помогло бы:

var test = from elt in TableSettings.Descendants("Table") 
      let value = elt.Attribute("name").Value 
      where argsDict.TryGetValue(value, out var v) 
      select new {elt, v}; 
+0

в этом случае лучше использовать _HashSet_ вместо простого массива – Grundy

+0

@Grundy или словарь. –

+0

methinks здесь HashSet более прост и читаем :-) – Grundy

1

Есть два подхода для достижения того, что вы хотите, один с помощью Where и второй с помощью JOIN

Вы можете применить присоединиться к получить результат как

var mytest = TableSettings.Descendants("Table").Join(arggs, x => x.Attribute("name").Value, y => y, (x, y) => new { Element = x }); 

и вы можете применять Where как

TablesToSync = TableSettings.Descendants("Table").Where(x => arggs.Contains(x.Attribute("name").Value)).ToList(); 

Если ваши данные невелики, это не должно приводить к большим улучшениям производительности, к которым вы решите использовать. Но если у вас есть большие данные, читающие комментарии и другие материалы об этом, вы должны сами решить, к какому подходу вы хотите следовать.

+0

Причина для downvote? Это поможет мне в будущем? Спасибо –

+0

Это просто запрос «значение в списке». Соединение довольно чрезмерно. Просто 'Where (t => args.Contains (t))' вернет соответствующие элементы. Не говоря уже о том, что вы используете 'Join' здесь как инструкцию' Where' и 'Select'.Это немного запутанно, и в этом примере просто не нужно. Часть 'where' уже возвращает нужный тип –

+1

@PanagiotisKanavos, почему вы думаете, что это связано с чрезмерным? – Grundy

Смежные вопросы