I have this list of Debit object
List<Debit> debits = new List<Debit>()
{
new Debit { Code = "A001", Length = 100, Qte = 3, Position = "MCD" },
new Debit { Code = "A001", Length = 100, Qte = 2, Position = "MED" },
new Debit { Code = "A001", Length = 200, Qte = 1, Position = "MCG" },
new Debit { Code = "A002", Length = 200, Qte = 1, Position = "MCD" },
new Debit { Code = "A003", Length = 200, Qte = 1, Position = "TBD" }
};
and I try to group Debit by Code and Length, sum the Qte property of grouped lines and join the Position string separated by a comma ", " in a new list of Debit named sortedDebit.
sortedDebit :
Code = "A001", Length = 100, Qte = 5, Position = "MCD, MED"
Code = "A001", Length = 200, Qte = 1, Position = "MCG"
Code = "A002", Length = 200, Qte = 1, Position = "MCD"
Code = "A003", Length = 200, Qte = 1, Position = "TBD"
Is there a way using linq to do that?
Sure there is. You can group by the combination of the two fields into a new composite key, which is a simple anonymous object. This requires that each of the fields making up the new composite key is equatable either by object reference or because it has a valid GetHashCode and Equals implementation.
var grouped = debits
.GroupBy(d => new { Code = d.Code, Length = d.Length })
.Select(g => new Debit() {
Code = g.Key.Code,
Length = g.Key.Length,
Qte = g.Sum(x => x.Qte),
Position = string.Join(", ", g.Select(x => x.Position).Distinct())
});
Related
I am updating older code but part of it must stay the same. I have now picker that needs to be filled with list.
My list
public List<TimeoutBetweenSentences> FillTimoutOptions()
{
var newListTimeoutBetweenSentenceses = new List<TimeoutBetweenSentences>()
{
new TimeoutBetweenSentences()
{
Position = 0,
Text = "+ 0 sekund",
Value = 0
},
new TimeoutBetweenSentences()
{
Position = 1,
Text = "+ 1 sekunda",
Value = 1
},
new TimeoutBetweenSentences()
{
Position = 2,
Text = "+ 2 sekundy",
Value = 2
},
new TimeoutBetweenSentences()
{
Position = 3,
Text = "+ 3 sekundy",
Value = 3
},
new TimeoutBetweenSentences()
{
Position = 4,
Text = "+ 4 sekundy",
Value = 4
},
new TimeoutBetweenSentences()
{
Position = 5,
Text = "+ 5 sekund",
Value = 5
},
};
return newListTimeoutBetweenSentenceses;
}
List<TimeoutBetweenSentences> allOptions = FillTimoutOptions();
sentencePausesStepper.Items.Add(allOptions.Select(m => m.Text).ToList().ToString());
however this displays just as "System collections" DO zou have any idea?
this is adding an entire list as ONE element
sentencePausesStepper.Items.Add(allOptions.Select(m => m.Text).ToList().ToString());
to add elements of one list to another, use AddRange instead
sentencePausesStepper.Items.AddRange(allOptions.Select(m => m.Text).ToList().ToString());
or better, do this
sentencePausesStepper.ItemsSource = allOptions;
sentencePausesStepper.ItemDisplayBinding = new Binding("Text");
I'm trying to merge two arrays and sum the values having the same keys. Is it possible to do so?
public struct BassoValues
{
public int BassoId { get; set; }
public decimal Amount { get; set; }
public BassoValues(int bassoId, decimal amount)
{
BassoId = bassoId;
Amount = amount;
}
}
var arrayOne = new BassoValues[4]
arrayOne[0] = new BassoValues() { BassoId = 1, Amount = 1};
arrayOne[1] = new BassoValues() { BassoId = 2, Amount = 10};
arrayOne[2] = new BassoValues() { BassoId = 3, Amount = 20};
arrayOne[3] = new BassoValues() { BassoId = 4, Amount = 30};
var arrayTwo = new BassoValues[4]
arrayTwo[0] = new BassoValues() { BassoId = 1, Amount = 1};
arrayTwo[1] = new BassoValues() { BassoId = 2, Amount = 10};
arrayTwo[2] = new BassoValues() { BassoId = 3, Amount = 20};
arrayTwo[3] = new BassoValues() { BassoId = 4, Amount = 30};
I want to achieve the following result.
var arrayFinal = new BassoValues[4]
arrayFinal[0] = new BassoValues() { BassoId = 1, Amount = 2};
arrayFinal[1] = new BassoValues() { BassoId = 2, Amount = 20};
arrayFinal[2] = new BassoValues() { BassoId = 3, Amount = 40};
arrayFinal[3] = new BassoValues() { BassoId = 4, Amount = 60};
This is how I am trying to achieve the result:
for (int i = 0; i < arrayOne.Length; i++)
{
for (int j = 0; j < arrayTwo.Length; j++)
{
if (arrayOne[0].BassoId == arrayTwo[0].BassoId)
{
var bassoId = arrayOne[0].BassoId;
var sum = arrayOne[0].Amount + arrayTwo[0].Amount;
arrayFinal[0] = new BassoValues() { bassoId, sum};
}
}
}
It'll work in cases when some ids aren't contained in both arrays and if ids can repeat inside one array as well.
var result = arrayOne.Concat(arrayTwo).GroupBy(x => x.BassoId)
.Select(x => new BassoValues(x.Key, x.Sum(y => y.Amount)))
.ToArray();
One solution would be to join the arrays using the Id:
var sumarray = (from a1 in arrayOne
join a2 in arrayTwo on a1.BassoId equals a2.BassoId
select new BassoValues {BassoId = a1.BassoId, Amount = a1.Amount + a2.Amount}).ToArray();
EDIT: In case that each array can contain multiple entries with the same ID and you want to sum them up then the linq-join solution will not suffice anymore. You could group by the id and calculate the sums per id in a loop:
List<BassoValues> Result = new List<BassoValues>();
foreach (var element in arrayOne.GroupBy(x => x.BassoId))
{
BassoValues temp = new BassoValues {BassoId = element.Key};
temp.Amount = arrayTwo.Where(x => x.BassoId == temp.BassoId).Sum(x => x.Amount) + element.Sum(x => x.Amount);
Result.Add(temp);
}
You say your arrays' sizes are fixed to 4.
var arrayFinal = new BassoValues[4]; // create final array
// loop each array
for (int i = 0; i < 4; i++)
{
int amount = arrayOne[i].Amount + arrayTwo[i].Amount;
arrayFinal[i] = new BassoValues() { BassoId = (i+1), Amount = amount };
}
class Dimensions
{
public int b { get; set; }
public int d { get; set; }
public int CutLength { get; set; }
}
Public void FramingID()
{
var DimList = new List<Dimensions>();
DimList.Add(new Dimensions { b = 2, d = 4, CutLength=10});
DimList.Add(new Dimensions { b = 10,d = 5, CutLength=20});
DimList.Add(new Dimensions { b = 4, d = 6, CutLength=30});
DimList.Add(new Dimensions { b = 4, d = 2, CutLength=40});
DimList.Add(new Dimensions { b = 2, d = 2, CutLength=50});
DimList.Add(new Dimensions { b = 6, d = 4, CutLength=60});
DimList.Add(new Dimensions { b = 2, d = 2, CutLength=70});
DimList.Add(new Dimensions { b = 2, d = 5, CutLength=80});
DimList.Add(new Dimensions { b = 6, d = 2, CutLength=80});
DimList.Add(new Dimensions { b = 2, d = 2, CutLength=50});
var Order = from m in DimList orderby m.b, m.d, m.CutLength select m;
var Order = from m in DimList orderby m.b, m.d, m.CutLength select m;
foreach (var n in Order)
{
Console.WriteLine( n.b.ToString() + " x " + n.d.ToString() + " x " + n.CutLength.ToString());
}
}
result:
2 x 2 x 50
2 x 2 x 50
2 x 2 x 70
2 x 4 x 10
2 x 5 x 80
4 x 2 x 40
4 x 6 x 30
6 x 2 x 80
6 x 4 x 60
10 x 5 x 20
I am trying to create a multilevel list using the same logic in the code above but the difference is that the values in the list are not predefined values a. The result intended in the code below is the same as the code above but the values cannot be predefined
They are values that require to be searched from a group of elements and then be added to the list and be arranged in ascending order accordingly
How can I add the values from the list as integers without using a for loop or a foreach loop as both will no work with the ordering as values will be added separately
Thanks
class Dimensions
{
public int b { get; set; }
public int d { get; set; }
public int CutLength { get; set; }
}
Public void FramingID()
{
var doc = Application.ActiveUIDocument.Document;
FilteredElementCollector Collector = new FilteredElementCollector(doc);
ICollection<Element> StructuralFraming = Collector.OfClass(typeof(FamilyInstance)).OfCategory(BuiltInCategory.OST_StructuralFraming).ToList();
List<int> bIntegerList = (from Element element in StructuralFraming select Convert.ToInt32(doc.GetElement(element.GetTypeId()).LookupParameter("b").AsValueString())).ToList();
List<int> dIntegerList = (from Element element in StructuralFraming select Convert.ToInt32(doc.GetElement(element.GetTypeId()).LookupParameter("d").AsValueString())).ToList();
List<int> ClIntegerList = (from Element element in StructuralFraming select Convert.ToInt32(element.LookupParameter("Cut Length").AsValueString())).ToList();
var DimList = new List<Dimensions>();
DimList.Add(new Dimensions { b = bIntegerList, d = dIntegerList, CutLength = ClIntegerList});
var Order = from m in DimList orderby m.b, m.d, m.CutLength select m;
foreach (var n in Order)
{
TaskDialog.Show("TEST", n.b.ToString() + " x " + n.ToString() + " x " + n.ToString());
}
}
List<int> bIntegerList = new List<int> { 2, 5, 6, 3, 4 };
List<int> dIntegerList = new List<int> { 20, 60, 30, 40, 50 };
List<int> ClIntegerList = new List<int> { 300, 300, 200, 500, 600 };
var wrapperList = bIntegerList.Zip(dIntegerList, (b, d) => new { b, d });
var dimListReal = wrapperList.Zip(ClIntegerList, (w, cl) => new Dimensions() { b = w.b, d = w.d, CutLength = cl });
var Order = from m in dimListReal orderby m.b, m.d, m.CutLength select m;
foreach (var n in Order)
{
Console.WriteLine("Test working " + n.b.ToString() + " x " + n.d.ToString() + " x " + n.CutLength.ToString());
}
from revit
var doc = Application.ActiveUIDocument.Document;
FilteredElementCollector Collector = new FilteredElementCollector(doc);
ICollection<Element> StructuralFraming = Collector.OfClass(typeof(FamilyInstance)).OfCategory(BuiltInCategory.OST_StructuralFraming).ToList();
List<int> bIntegerList = new List<int> (from Element element in StructuralFraming select Convert.ToInt32(doc.GetElement(element.GetTypeId()).LookupParameter("b").AsValueString())).ToList();
List<int> dIntegerList = new List<int>(from Element element in StructuralFraming select Convert.ToInt32(doc.GetElement(element.GetTypeId()).LookupParameter("d").AsValueString())).ToList();
List<int> ClIntegerList = new List<int>(from Element element in StructuralFraming select Convert.ToInt32(element.LookupParameter("Cut Length").AsValueString())).ToList();
var wrapperList = bIntegerList.Zip(dIntegerList, (b, d) => new { b, d });
var dimListReal = wrapperList.Zip(ClIntegerList, (w, cl) => new Dimensions() { b = w.b, d = w.d, CutLength = cl });
var Order = from m in dimListReal orderby m.b, m.d, m.CutLength select m;
foreach (var n in Order)
{
TaskDialog.Show("Test", n.b.ToString() + " x " + n.d.ToString() + " x " + n.CutLength.ToString());
}
So you must have some identifier that relates the 3 dimensions as part of the same entity. Here we have ElementX that represents a Dimension. It can be b or d or CutLength. Each ElementX has an identifier that binds him to other dimension value. Example, if you submit a new Trio of dimensions it will look like:
ElementX dimensionB = new ElementX { Xvalue = 10 , Id = 999 }
ElementX dimensionD = new ElementX { Xvalue = 80 , Id = 999 }
ElementX dimensionCutLength = new ElementX { Xvalue = 800 , Id = 999 }
And there is the testing code
static void Main(string[] args)
{
List<ElementX> bIntegerList = new List<ElementX> { new ElementX { Xvalue = 6, Id = 77},
new ElementX { Xvalue = 3, Id = 66 },
new ElementX { Xvalue = 8, Id = 65 } };
List<ElementX> dIntegerList = new List<ElementX> { new ElementX { Xvalue = 30, Id = 66},
new ElementX { Xvalue = 60, Id = 77 },
new ElementX { Xvalue = 80, Id = 65 } };
List<ElementX> ClIntegerList = new List<ElementX> { new ElementX { Xvalue = 800, Id = 65},
new ElementX { Xvalue = 600, Id = 77 },
new ElementX { Xvalue = 300, Id = 66 } };
var wrapperList = bIntegerList.Join(dIntegerList,
x => x.Id,
y => y.Id,
(x, y) => new { b = x.Xvalue, d = y.Xvalue, Id = y.Id }).ToList();
var dimList = wrapperList.Join(ClIntegerList,
x => x.Id,
cl => cl.Id,
(x, cl) => new Dimensions { b = x.b, d = x.d, CutLength = cl.Xvalue }).ToList();
var Order = from m in dimList orderby m.b, m.d, m.CutLength select m;
foreach (var n in Order)
{
Console.WriteLine("Test working " + n.b.ToString() + " x " + n.d.ToString() + " x " + n.CutLength.ToString());
}
Output
var data = new[] {
new { Id = 0, Cat = 1, Price = 2 },
new { Id = 1, Cat = 1, Price = 10 },
new { Id = 2, Cat = 1, Price = 30 },
new { Id = 3, Cat = 2, Price = 50 },
new { Id = 4, Cat = 2, Price = 120 },
new { Id = 5, Cat = 2, Price = 200 },
new { Id = 6, Cat = 2, Price = 1024 },
};
var ranges = new[] { 10, 50, 100, 500 };
Needed output is grouped price count by equal or greater than the range used according categories.
(in one linq statement)
cat range count
-------------------------------------
1 10 2 (In 1. categories there is 2 item that price >= 10(range) [10;30])
2 10 4 (In 2. categories there is 4 item that price >= 10(range) [50;120;200;1024])
2 50 4 ....
2 100 3 ....
2 500 1 (In 2. categories there is 1 item that price >= 500(range) [1024])
Try this:
var data = new[] {
new { Id = 0, Cat = 1, Price = 2 },
new { Id = 1, Cat = 1, Price = 10 },
new { Id = 2, Cat = 1, Price = 30 },
new { Id = 3, Cat = 2, Price = 50 },
new { Id = 4, Cat = 2, Price = 120 },
new { Id = 5, Cat = 2, Price = 200 },
new { Id = 6, Cat = 2, Price = 1024 },
};
var ranges = new[] { 10, 50, 100, 500 };
var result = from r in ranges
from g in data
where g.Price >= r
select new {g.Cat, Price=r};
var groupedData =
from d in result
group d by new{d.Cat, d.Price} into g
select new{Cat=g.Key.Cat, Price=g.Key.Price, TotalCount=g.Count()};
This should work:
var values =
data.SelectMany(x => ranges.Where(y => x.Price >= y)
.Select(y => new { Record = x, Range = y }))
.GroupBy(x => new { Cat = x.Record.Cat, Range = x.Range })
.Select(x => new { Cat = x.Key.Cat, Range = x.Key.Range, Count = x.Count()});
Results:
{ Cat = 1, Range = 10, Count = 2 }
{ Cat = 2, Range = 10, Count = 4 }
{ Cat = 2, Range = 50, Count = 4 }
{ Cat = 2, Range = 100, Count = 3 }
{ Cat = 2, Range = 500, Count = 1 }
When trying to unit-test a methode I run into a problem. The task of the method is to move an node in an tree. This tree is build via the "Preorder Tree Traversal" method. http://blogs.sitepoint.com/hierarchical-data-database-2/ This methode uses Left and Right values of a node to position it in a tree.
In the methode the Lft and Rgt values will be changed for a node. In my testcase i'll be moving the node "Meat" under "Banana".
var Food = new Node { Id = Guid.NewGuid(), Title = "Food", Ordinal = 0,Depth = 0, Lft = 1, Rgt = 18};
var Fruit = new Node { Id = Guid.NewGuid(), Title = "Fruit", ParentNode = Food, Depth = 1, Ordinal = 0, Lft = 2, Rgt = 11 };
var Red = new Node { Id = Guid.NewGuid(), Title = "Red", ParentNode = Fruit, Depth = 2, Ordinal = 0, Lft = 3, Rgt = 6 };
var Cherry = new Node { Id = Guid.NewGuid(), Title = "Cherry", ParentNode = Red, Depth = 3, Ordinal = 0, Lft = 4, Rgt = 5 };
var Yellow = new Node { Id = Guid.NewGuid(), Title = "Yellow", ParentNode = Fruit, Depth = 2, Ordinal = 1, Lft = 7, Rgt = 10 };
var Banana = new Node { Id = Guid.NewGuid(), Title = "Banana", ParentNode = Yellow, Depth = 3, Ordinal = 0, Lft = 8, Rgt = 9 };
var Meat = new Node { Id = Guid.NewGuid(), Title = "Meat", ParentNode = Food, Depth = 1, Ordinal = 1, Lft = 12, Rgt = 17 };
var Beef = new Node { Id = Guid.NewGuid(), Title = "Beef", ParentNode = Meat, Depth = 2, Ordinal = 0, Lft = 13, Rgt = 14 };
var Pork = new Node { Id = Guid.NewGuid(), Title = "Pork", ParentNode = Meat, Depth = 2, Ordinal = 1, Lft = 15, Rgt = 16 };
var allNodes = new List<Node> {Food, Fruit, Red, Cherry, Yellow, Banana, Meat, Beef, Pork};
var descendantsOfNodeFood = new List<Node>
{
Beef,
Cherry,
Red,
Yellow,
Pork,
Banana,
Meat,
Fruit
};
var descendantsOfNodeFruit = new List<Node>
{
Red,
Cherry,
Banana,
Yellow
};
var descendantsOfNodeRed = new List<Node>
{
Cherry
};
var descendantsOfNodeCherry = new List<Node> { };
var descendantsOfNodeYellow = new List<Node>
{
Banana
};
var descendantsOfNodeBanana = new List<Node> { };
var descendantsOfNodeMeat = new List<Node>
{
Beef,
Pork
};
var descendantsOfNodeBeef = new List<Node> { };
var descendantsOfNodePork = new List<Node> { };
//Mock the node repository
_mockNodeRepository.Setup(x => x.LoadNode(Food.Id)).Returns(Food);
_mockNodeRepository.Setup(x => x.GetDescendants(Food.Id, It.IsAny<bool>())).Returns(descendantsOfNodeFood);
_mockNodeRepository.Setup(x => x.GetDescendants(Fruit.Id, It.IsAny<bool>())).Returns(descendantsOfNodeFruit);
_mockNodeRepository.Setup(x => x.GetDescendants(Red.Id, It.IsAny<bool>())).Returns(descendantsOfNodeRed);
_mockNodeRepository.Setup(x => x.GetDescendants(Cherry.Id, It.IsAny<bool>())).Returns(descendantsOfNodeCherry);
_mockNodeRepository.Setup(x => x.GetDescendants(Yellow.Id, It.IsAny<bool>())).Returns(descendantsOfNodeYellow);
_mockNodeRepository.Setup(x => x.GetDescendants(Banana.Id, It.IsAny<bool>())).Returns(descendantsOfNodeBanana);
_mockNodeRepository.Setup(x => x.GetDescendants(Meat.Id, It.IsAny<bool>())).Returns(descendantsOfNodeMeat);
_mockNodeRepository.Setup(x => x.GetDescendants(Beef.Id, It.IsAny<bool>())).Returns(descendantsOfNodeBeef);
_mockNodeRepository.Setup(x => x.GetDescendants(Pork.Id, It.IsAny<bool>())).Returns(descendantsOfNodePork);
_mockNodeRepository.Setup(x => x.GetNodes()).Returns(allNodes);
When running the test all values are set to the correct ones, except the Lft and Rgt values of Meat. (They should be 9 - 10) I traced the problem back to the mock of the methode "GetDescendants(Meat.Id, It.IsAny()))". This methode will be called twice in the method i try to test. The first time it should return a list with the nodes "Beef" and "Pork". The second time, node "Beef" is placed under "Banana", it should return a list with only the node "Pork".
Who can help me figure out how the mock can return a list with 2 nodes the first time, and the list with only one node the second time?
I'm using "Microsoft.VisualStudio.TestTools.UnitTesting;" and "Moq;" for unit testing.
returning different values on each invocation
var mock = new Mock<IFoo>();
var calls = 0;
mock.Setup(foo => foo.GetCountThing())
.Returns(() => calls)
.Callback(() => calls++);
// returns 0 on first invocation, 1 on the next, and so on
Console.WriteLine(mock.Object.GetCountThing());
from: http://code.google.com/p/moq/wiki/QuickStart
However if your mock setup is that complex it may be better to use an in memory db or whatever you are using instead. Mocking logic is very fragile and hard to read.
To answer your comment:
var mock = new Mock<IFoo>();
bool firstCall = true;
mock.Setup(foo => foo.GetCountThing())
.Returns(() => firstCall ? list1 : list2)
.Callback(() => firstCall = false);
should return the first list then the second one for all following calls.