I have an array of IEnumerable (IEnumerable[]) and would like to generate all possible combinations of the elements in these IEnumerables.
It is similar to this problem: Generating Permutations using LINQ
but I do not know how many IEnumerables I will have beforehand and thus cannot use the described LINQ statement.
To give an example:
My array
IEnumerable[] array;
has for example these elements
array[0]={0,1,2};
array[1]={a,b};
Then I would like these to be returned:
{{0,a},{0,b},{1,a},{1,b},{2,a},{2,b}}
But it might also hold:
array[0]={0,1,2};
array[1]={a,b};
array[2]={w,x,y,z};
Then I would need the appropriate permutations. I do not have any information on how many IEnumerables and no information on how many elements each IEnumerable holds.
Thanks in advance for any help,
Lars
Seems like you want the Cartesian_product. This should work, although I didn't look particularly carefully at edge-cases
public static IEnumerable<T> Drop<T>(IEnumerable<T> list, long numToDrop)
{
if (numToDrop < 0) { throw new ArgumentException("Number to drop must be non-negative"); }
long dropped = 0;
var iter = list.GetEnumerator();
while (dropped < numToDrop && iter.MoveNext()) { dropped++; }
while (iter.MoveNext()) { yield return iter.Current; }
}
public static IEnumerable Concat(object head, IEnumerable list)
{
yield return head;
foreach (var x in list) { yield return x; }
}
public static IEnumerable<IEnumerable> CrossProduct(IEnumerable<IEnumerable> lists)
{
if (lists == null || lists.FirstOrDefault() == null) { yield break; }
var heads = lists.First();
var tails = CrossProduct(Drop(lists, 1));
if (tails.FirstOrDefault() == null)
{
foreach (var h in heads) { yield return new object[] { h }; }
}
else
{
foreach (var head in heads)
{
foreach (var tail in tails)
{
yield return Concat(head, tail);
}
}
}
}
Try this as a direction (you'll need to modify, I'm sure, and I haven't compiled it, so there may be some syntax errors)
public IEnumerable<string> CompileCominations
(IEnumberable<string[]> arrays, List<string> combinations)
{
if(combinations == null) combinations = new List<string>();
for(int i = arrays.Count() - 1; i >= 0; i--)
{
if(combinations.Count >= 1) combinations =
Combine2Lists(combinations, arrays[i]);
else
{
combinations = Combine2Lists(arrays[i], arrays[i -1];
i--;
}
}
return combinations;
}
private List<string> Combine2Lists
(IEnumberable<string> list1, IEnumerable<string> list2)
{
List<string> currentList = new List<string>();
for(int i = 0; i < list1.Count(); i ++)
{
for(int j = 0; j < list2.Count(); j++)
{
currentList.Add(
string.Format("{0},{1}", list1[i], list2[j]);
}
}
return currentList;
}
Again, not compiled, but what it should do is keep adding "item1, item2" to a list with a where item1 = the old "item1, item2" and item2 = just the new entry from the nth array.
Thus string array [a, b, c] + string array [1, 2, 3, 4] should yield: {a, 1}, {a, 2}, {a, 3}, {a, 4}, {b, 1}... and adding string array [x, y, z] to the first to would then yield: {a, 1, x}, {a, 1, y}, {a, 1, z} and so forth.
Related
I'm trying to achieve a method that I can call to create multiple items
This is the method I'm trying to get it to work
public void AddMultipleItems(string[] itemKey, int[] amount)
{
for (int i = 0; i < itemKey.Length; i++)
{
Item item;
item = ItemCollection[itemKey[i]];
for (int x = 0; x < amount.Length; x++)
{
if (inventory.CanAddItem(item, amount[x]) == true)
{
inventory.AddItem(item.GetCopy());
}
else if (inventory.CanAddItem(item, amount[x]) == false)
{
Debug.Log("Inventory Full");
break;
}
}
}
}
And then this method will be called to add in items like this:
itemDB.AddMultipleItems(new string[] { "Boots", "Gold Coin", "Apple" }, new int[] {1, 2, 3 });
Result: I get 3 Boots, 3 Gold coin and 3 Apple. when I should be getting 1 Boots, 2 Gold Coin & 3 Apples,
I've done a similar Method like this except it doesn't require array parameters and it works perfectly:
public void AddItems(string itemKey, int amount)
{
//First check if entered itemKey parameter exists in ItemCollection Dictionary
if (ItemCollection.ContainsKey(itemKey))
{
Item item;
item = ItemCollection[itemKey];
if (item != null)
{
//check if we can add the item and the amount of it
if (inventory.CanAddItem(item, amount))
{
//loop through the total amount
for (int i = 0; i < amount; i++)
{
//add the item
inventory.AddItem(item.GetCopy());
}
Debug.Log(itemKey + " Added Successfully!");
}
else
{
Debug.Log("Not enough space in Inventory");
}
}
else
{
Debug.Log("Null Item");
}
}
else
{
Debug.Log(itemKey + " does not exist in ItemDatabase's Dictionary");
}
}
So basically another way of looking at it is how can I turn the AddItems(string itemKey, int amount) into AddItems(string[] itemKey, int[] amount). its the for loop, foreach loop and arrays that are kinda tripping me over since I'm not very good at those.
Appreciate any help, Thank you!
For every item you are iterating over the entire amount array which has 3 entries. I would actually expect that for each item you get 1+2+3 = 6 items added.
Wouldn't you rather want to only get the amount to add from the one entry in amount with the same index as the given itemKey: amount[i]
public void AddMultipleItems(string[] itemKey, int[] amount)
{
for (int i = 0; i < itemKey.Length; i++)
{
Item item;
item = ItemCollection[itemKey[i]];
if (inventory.CanAddItem(item, amount[i]))
{
inventory.AddItem(item.GetCopy());
}
else // your if condition here was redundant
{
Debug.Log("Inventory Full");
}
}
}
In general you already have a method for adding a single item so I wouldn't re-implement that for multiple ones but rather call it from the loop like
public void AddMultipleItems(string[] itemKey, int[] amount)
{
for (int i = 0; i < itemKey.Length; i++)
{
// Instead of re-implement rather call your existing method
// and treat each value pair as if ti was a single add call
AddItems(itemKey[i], amount[i]);
}
}
Then passing in two separate arrays is pretty error prone and hard to maintain. I would probably rather pass in a Dictionary<string, int> like e.g.
public void AddMultipleItems(Dictionary<string, int> itemsToAdd)
{
foreach (var kvp in itemsToAdd)
{
AddItems(kvp.Key, kvp.Value);
}
}
and then call it like
itemDB.AddMultipleItems(new Dictionary<string, int>{ {"Boots", 1}, {"Gold Coin", 2}, {"Apple", 3}});
I have a extension method to judge if an item exists in the array/list.
I encountered a problem that how to judge the equality between two elements if the T is an Array/List?
Suppose [1, 2] and [2, 1] are two different elements.
Here is my method:
public static bool ContainsElement<T>(this IEnumerable<T> list, T target, out T matched)
{
matched = default(T);
if (typeof(T).IsCollection())
{
var targetElements = (target as IEnumerable).Cast<object>();
foreach (var element in list)
{
var innerElements = (element as IEnumerable).Cast<object>();
if (targetElements.Count() == innerElements.Count())
{
bool findDifference = false;
for (int i = 0; i < targetElements.Count(); i++)
{
if (/* How to compare the two element */)
{
findDifference = true;
}
}
if (!findDifference)
{
matched = element;
return true;
}
}
else
{
return false;
}
}
return false;
}
else
{
foreach (var element in list)
{
if (EqualityComparer<T>.Default.Equals(element, target))
{
matched = element;
return true;
}
}
return false;
}
}
I cannot find the way to know How to create a EqualityComparer<> for the element of the T array.
I can just get the type of element via typeof(T).GetElementType(), but it seems no use to compare the two element.
By the way, the IsCollection method is my extension method to check if the T is array or assigned from IEnumerable.
Can you help me?
i'm trying to get all of the array selected to be returned but
return breaks the loop, not allowed to use println etc, Its an array inside of an array called from a website, it only prints the first element then the loop breaks. Any help appreciated
def String citylist(String[][]data){
String result = null;
for(int i = 0; i < data.length; i++){
result = data[i][0];
return result;
}
}
I'm going to guess you want the first element of every array... it's hard to tell from the question...
If that's the case, you can do:
List<String> citylist(String[][]data){
data.collect { it[0] }
}
To get the first element of every array, this is what you need to do:
String[] citylist(String[][] data) {
String[] result = new String[data.length]
for (int i = 0; i < data.length; i++) {
result[i] = data[i][0]
}
return result
}
I was not sure wether your method signature is correct or not, so I changed it to return an array of String. But if you want a String you can do:
String citylist(String[][] data) {
String[] result = new String[data.length]
for (int i = 0; i < data.length; i++) {
result[i] = data[i][0]
}
return result.toString()
}
Same think if you can use a List:
def citylist(String[][] data) {
def result = []
for (int i = 0; i < data.length; i++) {
result << data[i][0]
}
return result
}
And then you can even use some nice Groovy features:
def citylist(String[][] data) {
def result = []
data.each {
result << it[0]
}
return result
}
For all above code the result will be [1, 11, 21]
If I understand your last comment, this is another way to do:
String[][] citylist(String[][] data) {
String[][] result = new String[data.length][]
for (int i = 0; i < data.length; i++) {
result[i] = new String[1]
result[i][0] = data[i][0]
}
return result
}
Your result will be [[1], [11], [21]]
I've to create a function (using pseudocode) which returns depth of specified element in an array (with optional arrays inside), e.g.:
def array[] = {"a", {"b", {"c"}, "d"}, {{{}}}, "e"};
For "e" it should returns 0, for "c" it should returns 2 etc.
If there's no specified element in array, function should returns -1.
I've tried a few times, but I've no idea for elegant (and working..) solution, only this:
func foo(array[], var element) {
int depth = 0;
boolean found = false;
boolean recursion = false;
boolean foundInRecursion = false;
for (int i = 0; i < array.length; i++) {
if (array[i] instanceof array[]) {
depth++;
recursion = true;
foo(array[i], element);
recursion = false;
} else if (array[i] == element) {
if (recursion) {
foundInRecursion = true;
} else {
found = true;
}
}
}
if (foundInRecursion) {
return depth;
} else if (found){
return 0;
} else {
return -1;
}
}
I would really appreciate any help!
Thanks
In your pseudocode:
func depth(array[], var element) {
/* this function returns how deep the element is in the given array */
for (int i = 0; i < array.length; i++) {
current = array[i]
if current == element {
return 0; // element is right in the array we are given
}
if (current instanceof array[]) {
// Whoa, subarray! How deep our element is in it?
subdepth = depth(current, element)
if subdepth >= 0 {
// Element was found in *sub*-array, so add 1 to the depth
return subdepth + 1;
}
}
}
// If we are here, we found nothing
return -1;
}
I believe that such elegant solution should be an iterative code that goes over each layer.
public int IterativeDepth(List<Object> lst, T element)
{
// Set the first iteration
int depth = 0;
List<Object> next = new List<Object>();
while ( ! IsNullOrEmpty(lst) )
// For each layer
{
foreach (var current in lst)
// For each element of a layer
{
if (current instanceof T && current == element)
// Found it, return the depth
{
return depth;
}
if (current instanceof List) {
// It's a list, append it to the next iteration
next.Add(current);
}
}
// Set the next iteration
lst = next;
next.clear();
depth += 1;
}
// Found nothing
return -1;
}
does anyone have any idea what to do in order to find the number of sequences in an array?
for example, my array is:
var numbers:Array = new Array(banana, banana, apple, banana, banana);
and i need to find is:
* how many times there is a sequence of "banana"
* and the length of each sequence.
what shell i do in order to get the following result:
2,1,2 (2 bananas, 1 apple, 2 bananas)
i tried with do while loop, but i i guess i miss something.
a short example will be very appreciated!
thanx
var prev:String = null;
var q:int = 0;
var result:Array = new Array();
for(var i:int=0; i<numbers.length; ++i){
if(prev!=numbers[i]){
if(q>0) result.push(q);
q=1;
prev=numbers[i];
}
else ++q;
}
if(q>0) result.push(q);
This is, assuming banana, etc. are strings (probably a typo above?). It would be simple to modify to other types of objects
Really all you want to know is whether the string at index n equals the string at index n+1...
var targetIndex:int = numbers.length - 1;
var results:Array = [1];
var resultsIndex:int = 0;
for(var n:int = 0; n < targetIndex; n++) {
if(numbers[n] == numbers[n+1]) {
results[resultsIndex]++;
} else {
results[++resultsIndex] = 1;
}
}
trace(results.join(','));
function sequencesInArray(array:Array):Array {
var sequence:Array = [];
var currSequenceCount:uint = 1;
for (var i:uint = 1; i < numbers.length; i++) {
if (numbers[i - 1] != numbers[i]) {
sequence.push(currSequenceCount);
currSequenceCount = 1;
} else {
currSequenceCount++;
}
}
return sequence;
}
Then:
var banana:int = 1;
var apple:int = 2;
sequencesInArray([banana, banana, apple, banana, banana]); //returns: [2, 1, 2]
In the question you don't define banana and apple, anyway I would use a map or a Dictionary to store key/value pairs, with the key being the string/object you want to count and the value being the counter of the object occurences in your array.
var objectsCounter:Dictionary = new Dictionary();
for (var key:String in numbers)
{
if ( objectsCounter[key] )
objectsCounter[key] = objectsCounter[key] + 1;
else
objectsCounter[key] = 1;
}
This way you can store any type in the dictionary.
edit:
for (var key:String in objectsCounter)
{
// iterates through each object key
}
for each (var value:Number in objectsCounter)
{
// iterates through each value
}
I believe this is what you're looking for:
var array:Array = [
"banana", "banana",
"apple",
"banana", "banana", "banana"
];
var sequences:Array = findSequences(array, "banana");
trace("sequences:", sequences); // prints "sequences: 2,3"
And:
private function findSequences(array:Array, searchElement:*):Array
{
var sequences:Array = [];
var currentSequence:int = 0;
for each (var element:* in array) {
if (element == searchElement) {
currentSequence++;
} else if (currentSequence > 0) {
sequences.push(currentSequence);
currentSequence = 0;
}
}
if (currentSequence > 0) {
sequences.push(currentSequence);
}
return sequences;
}