#c# #for-loop #cartesian-product
#c# #for-loop #Декартово произведение
Вопрос:
У меня есть 3 списка, содержащих строки
GenderList = ["M", "F"]
AgeList = ["10-15", "16-20", "21-26"]
CityList = ["CityA", "CityB"]
Я хочу создать перекрестный продукт над списками, но катастрофически:
если пользователь запрашивает gender_city
:
Мне нужно следующее:
foreach(var g in GenderList)
foreach(var c in CityList
.....
Если пользователь запрашивает gender_city_age
, цикл должен быть:
foreach(var g in GenderList)
foreach(var c in CityList)
foreach(var a in AgeList)
....
если пользователь запрашивает city_age
foreach(var c in CityList)
foreach(var a in AgeList)
...
Как я могу это сделать? У меня есть о 20
списках, мне нужно динамически умножать элементы списков
спасибо
Комментарии:
1. Что вы хотите сделать, объединить строки, создать объект со всеми свойствами, …?
2. Мне любопытно, может ли быть лучший подход, чем использование набора циклов for. Я не совсем уверен, какие данные вам нужны, но это звучит так, что Sql Server или какая-либо СУБД могут быть более подходящими для того, что вам нужно, поскольку у вас могут быть условные соединения СЛЕВА, которые могут дать вам то, что вам нужно.
3. Да, у меня есть 3 таблицы из базы данных (пол, город, возраст), и я заполняю их в списки. Я хочу предоставить пользователю подсчет (пол в городе), (пол по возрасту), (пол по городу и возрасту) Я имею в виду, я хочу, чтобы пользователь заполнял лист Excel, содержащий динамический col: gender city coutn или gender city age count, или city age count …. и т. Д
4. связаны ли ваши данные внешними ключами в таблицах вашей базы данных?
5. Нет, отдельные таблицы, я хочу генерировать Excel динамически
Ответ №1:
Давайте начнем с обобщенного Cartesian
:
private static IEnumerable<T[]> Cartesian<T>(IEnumerable<IEnumerable<T>> source) {
T[][] lists = source
.Select(line => line.ToArray())
.ToArray();
if (source.Any(line => !line.Any()))
yield break;
int[] indexes = new int[lists.Length];
do {
yield return lists.Select((line, index) => line[indexes[index]]).ToArray();
for (int i = 0; i < indexes.Length; i)
if ( indexes[i] < lists[i].Length)
break;
else
indexes[i] = 0;
}
while (!indexes.All(index => index == 0));
}
Тогда мы можем попробовать его использовать. Давайте организуем все коллекции, которые мы хотим запросить, в единый словарь:
// we allow to use any IEnumerable<string>, not necessary List<string>
Dictionary<string, IEnumerable<string>> data =
new Dictionary<string, IEnumerable<string>>(StringComparer.OrdinalIgnoreCase) {
{ "Gender", new [] { "M", "F" } },
{ "Age", new List<string> { "10-15", "16-20", "21-26" } },
{ "City", new [] { "CityA", "CityB" } },
};
И запрашивать его:
string userChoice = "gender_city_age";
var result = Cartesian(userChoice.Split('_').Select(name => data[name]));
// Have a look at the result:
string report = string.Join(Environment.NewLine, result
.Select(line => string.Join(", ", line)));
Console.Write(report);
Результат:
M, CityA, 10-15
F, CityA, 10-15
M, CityB, 10-15
F, CityB, 10-15
M, CityA, 16-20
F, CityA, 16-20
M, CityB, 16-20
F, CityB, 16-20
M, CityA, 21-26
F, CityA, 21-26
M, CityB, 21-26
F, CityB, 21-26
Комментарии:
1. Я постараюсь, кажется отличным ответом, большое вам спасибо