Creating combinations sku product C# - combinations

I've to figure out how to generate SKUs for a product with a certain number (x) of attributes and each attribute has a certain number (y) of values. For example:
A t-shirt has the following attributes:
Color
Size
Gender
Sleeve
And the values for those attributes are:
Color: Red, White, Blue
Size: S, M, L, XL
Gender: M, F
Sleeve: Short, Long
I need to create a SKU for each unique combination e.g. SKU #: 1234 for Small, Red, Male w/ short sleeves, SKU #: 2345 for Small, Red, Male, Long Sleeve.
My Class:
public class Sku
{
public int Id { get; set; }
public int ProductId { get; set; }
public string Name { get; set; }
public List<SkuDetail> SkuDetails { get; set; };
}
public class SkuDetail
{
public int Id { get; set; }
public int SkuId { get; set; }
public string Name { get; set; }
}
Demo data:
var skus = new List<Sku>();
skus.Add(new Sku
{
Name = "Size",
SkuDetails = new List<SkuDetail>
{
new SkuDetail {Name = "S"}, new SkuDetail {Name = "M"},
new SkuDetail {Name = "L"}, new SkuDetail {Name = "XL"}, new SkuDetail {Name = "XXL"}
}
});
skus.Add(new Sku
{
Name = "Color",
SkuDetails = new List<SkuDetail>
{
new SkuDetail {Name = "Red"}, new SkuDetail {Name = "White"},
new SkuDetail {Name = "Black"}
}
});
skus.Add(new Sku
{
Name = "Style",
SkuDetails = new List<SkuDetail>
{new SkuDetail {Name = "Modern"}, new SkuDetail {Name = "classic"}}
});
I'd like to use the most efficient approach but I can only think of nested foreach statements for this. I could use some help with the logic here.

Related

How to get distinct values from 2 different columns in the same list

So as you can see from the code bellow i have a list object named Matches, from which i would like to get a single list of the distinct teams, both from HomeTeam and AwayTeam. I'm trying to use LINQ and i can get a list of distinct teams if i only use HomeTeam parameter or AwayTeam parameter but not both together.
Thank you.
public class Match
{
public int ID { get; set; }
public string Country { get; set; }
public string Championship { get; set; }
public string Seasson { get; set; }
public DateTime MatchDate { get; set; }
public string HomeTeam { get; set; }
public int HomeScore { get; set; }
public int AwayScore { get; set; }
public string AwayTeam { get; set; }
}
private List<Match> Matches;
Matches = dataAccess.GetAllMatches();
I'm Trying to do something like that:
result = Matches.Select(HomeTeam, AwayTeam).Distinct().ToList();
At the risk that this smells like homework, a hint rather than code. Get your Home teams, Union your Away teams and apply a Distinct to the result.
So i finally come up with this solution.
Notice that now i need also to get not only the team but the country which the team belongs to.
public class Team
{
public string Name { get; set; }
public string Country { get; set; }
}
So Union really do the job here but since now i need to get it as an anonymous type... here is the code:
List<Team> teams = new List<Team>();
var result = Matches.Select(x => new { Name = x.HomeTeam, Country = x.Country }).Union(Matches.Select(x => new { Name = x.AwayTeam, Country = x.Country })).ToList();
foreach (var record in result)
{
teams.Add(new Team { Name = record.Name, Country = record.Country });
}
return teams;
I would prefer this way:
List<Team> teamsResult = Matches.Select(x => new Team { Name = x.HomeTeam, Country = x.Country }).Union(Matches.Select(x => new Team { Name = x.AwayTeam, Country = x.Country })).ToList();
But this way get duplicates so i will stick with the first example for now.
Do you think it is the more elegant way to go?
Thank you.
You can take advantage of GroupBy, like this:
IEnumerable<Team> teams = Matches.GroupBy(m => new { m.AwayTeam, m.HomeTeam, m.Country })
.Select(
g =>
new[]
{
new Team {Country = g.Key.Country, Name = g.Key.AwayTeam},
new Team {Country = g.Key.Country, Name = g.Key.HomeTeam}
})
.SelectMany(x => x)
.GroupBy(t => new { t.Name, t.Country })
.Select(g => new Team { Name = g.Key.Name, Country = g.Key.Country });

Ravendb: map-reduce on two relating documents

In my ravendb I have 2 documents: Country and City
City document looks like this
Id
Name
CountryId
Country document looks like this
Id
Name
At the moment I have an index where I retrieve all cities as a list and this works.
But I would rather want to retrieve all cities grouped by countries
This is what I have
public class City_ByCountry
{
public string CityId { get; set; }
public string CityName { get; set; }
public string CountryName { get; set; }
}
Map = (city => from cit in city
let cou = LoadDocument<Country>(cit.CountryId)
select new City_ByCountry
{
CityId = cit.Id,
CityName = cit.Name,
CountryName = cou.Name
});
This works but gives me a list of all cities (id, name, countryName)
But I want a list like this
CountryName [
List with cities]
CountryName [
List with cities]
etc
Can I do this with a reduce on the result? Or what is the correct way to do this?
I think with a reduce this is possible. See Ayende's post Awesome indexing with RavenDB on how to perform advanced indexing.
I tried to modify Ayende's example to match your needs (just on Notepad, so I don't know if it even compiles):
public class City_ByCountry : AbstractIndexCreationTask<City, City_ByCountry.ReduceResult>
{
public class ReduceResult
{
public string CountryId { get; set; }
public string CountryName { get; set; }
public City[] Cities { get; set; }
}
public City_ByCountry()
{
Map = cities =>
from city in cities
select new
{
CountryId= city.CountryId,
CountryName = LoadDocument<Country>(city.CountryId),
Cities = new [] { city }
};
Reduce = cities =>
from city in cities
group city by city.CountryId
into g
select new
{
CountryId = g.Key,
CountryName = g.First().CountryName,
Cities = g.SelectMany(x => x.Cities)
};
}
}

Moq unit test to filter products by their categories

I am new to unit testing so I am sure this is a very basic question, but I couldn't find a solution when I searched for it.
I am trying to test to see if I can filter products by their categories. I can access all the properties in my Product class but not the ones in my Category class. For example, it doesn't find Category1.Name. Can anyone tell me what I'm doing wrong?
This is my product class;
public partial class Product
{
public int ProductID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public decimal Price { get; set; }
public int CategoryID { get; set; }
public virtual Category Category1 { get; set; }
}
This is my test;
[TestMethod]
public void Can_Filter_Products()
{
//Arrange
Mock<IProductRepository> mock = new Mock<IProductRepository>();
mock.Setup(m => m.Products).Returns(new Product[]
{
new Product {ProductID=1,Name="P1", **Category1.Name** = "test1" },
new Product {ProductID=2,Name="P2", **Category1.Name** = "test2"},
new Product {ProductID=3,Name="P3", **Category1.Name** = "test1"},
new Product {ProductID=4,Name="P4", **Category1.Name** = "test2"},
new Product {ProductID=5,Name="P5", **Category1.Name** = "test3"},
}.AsQueryable());
//Arrange create a controller and make the page size 3 items
ProductController controller = new ProductController(mock.Object);
controller.PageSize = 3;
//Action
Product[] result = ((ProductsListViewModel)controller.List("test2", 1).Model).Products.ToArray();
//Assert - check that the results are the right objects and in the right order.
Assert.AreEqual(result.Length, 2);
Assert.IsTrue(result[0].Name == "P2" && result[0].Category1.Name == "test2");
Assert.IsTrue(result[1].Name == "P4" && result[1].Category1.Name == "test2");
}
In your mock setup, try this instead:
mock.Setup(m => m.Products).Returns(new[]
{
new Product {ProductID=1,Name="P1", Category1 = new Category { Name = "test1"} },
new Product {ProductID=2,Name="P2", Category1 = new Category { Name = "test1"} }
}.AsQueryable());

RavenDB MultiMapReduce Sum not returning the correct value

Sorry for this lengthy query, I decided to add the whole test so that it will be easier for even newbies to help me with this total brain-melt.
The using directives are:
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using Raven.Client;
using Raven.Client.Embedded;
using Raven.Client.Indexes;
Please leave feedback if I'm too lengthy, but what could possibly go wrong if I add a complete test?
[TestFixture]
public class ClicksByScoreAndCardTest
{
private IDocumentStore _documentStore;
[SetUp]
public void SetUp()
{
_documentStore = new EmbeddableDocumentStore {RunInMemory = true}.Initialize();
_documentStore.DatabaseCommands.DisableAllCaching();
IndexCreation.CreateIndexes(typeof (ClicksBySearchAndProductCode).Assembly, _documentStore);
}
[TearDown]
public void TearDown()
{
_documentStore.Dispose();
}
[Test]
public void ShouldCountTotalLeadsMatchingPreference()
{
var userFirst = new User {Id = "users/134"};
var userSecond = new User {Id = "users/135"};
var searchFirst = new Search(userFirst)
{
Id = "searches/24",
VisitId = "visits/63"
};
searchFirst.Result = new Result();
searchFirst.Result.Rows = new List<Row>(
new[]
{
new Row {ProductCode = "CreditCards/123", Score = 6},
new Row {ProductCode = "CreditCards/124", Score = 4}
});
var searchSecond = new Search(userSecond)
{
Id = "searches/25",
VisitId = "visits/64"
};
searchSecond.Result = new Result();
searchSecond.Result.Rows = new List<Row>(
new[]
{
new Row {ProductCode = "CreditCards/122", Score = 9},
new Row {ProductCode = "CreditCards/124", Score = 4}
});
var searches = new List<Search>
{
searchFirst,
searchSecond
};
var click = new Click
{
VisitId = "visits/64",
ProductCode = "CreditCards/122",
SearchId = "searches/25"
};
using (var session = _documentStore.OpenSession())
{
foreach (var search in searches)
{
session.Store(search);
}
session.Store(click);
session.SaveChanges();
}
IList<ClicksBySearchAndProductCode.MapReduceResult> clicksBySearchAndProductCode = null;
using (var session = _documentStore.OpenSession())
{
clicksBySearchAndProductCode = session.Query<ClicksBySearchAndProductCode.MapReduceResult>(ClicksBySearchAndProductCode.INDEX_NAME)
.Customize(x => x.WaitForNonStaleResults()).ToArray();
}
Assert.That(clicksBySearchAndProductCode.Count, Is.EqualTo(4));
var mapReduce = clicksBySearchAndProductCode
.First(x => x.SearchId.Equals("searches/25")
&& x.ProductCode.Equals("CreditCards/122"));
Assert.That(mapReduce.Clicks,
Is.EqualTo(1));
}
}
public class ClicksBySearchAndProductCode :
AbstractMultiMapIndexCreationTask
<ClicksBySearchAndProductCode.MapReduceResult>
{
public const string INDEX_NAME = "ClicksBySearchAndProductCode";
public override string IndexName
{
get { return INDEX_NAME; }
}
public class MapReduceResult
{
public string SearchId { get; set; }
public string ProductCode { get; set; }
public string Score { get; set; }
public int Clicks { get; set; }
}
public ClicksBySearchAndProductCode()
{
AddMap<Search>(
searches =>
from search in searches
from row in search.Result.Rows
select new
{
SearchId = search.Id,
ProductCode = row.ProductCode,
Score = row.Score.ToString(),
Clicks = 0
});
AddMap<Click>(
clicks =>
from click in clicks
select new
{
SearchId = click.SearchId,
ProductCode = click.ProductCode,
Score = (string)null,
Clicks = 1
});
Reduce =
results =>
from result in results
group result by
new { SearchId = result.SearchId, ProductCode = result.ProductCode }
into g
select
new
{
SearchId = g.Key.SearchId,
ProductCode = g.Key.ProductCode,
Score = g.First(x => x.Score != null).Score,
Clicks = g.Sum(x => x.Clicks)
};
}
}
public class User
{
public string Id { get; set; }
}
public class Search
{
public string Id { get; set; }
public string VisitId { get; set; }
public User User { get; set; }
private Result _result = new Result();
public Result Result
{
get { return _result; }
set { _result = value; }
}
public Search(User user)
{
User = user;
}
}
public class Result
{
private IList<Row> _rows = new List<Row>();
public IList<Row> Rows
{
get { return _rows; }
set { _rows = value; }
}
}
public class Row
{
public string ProductCode { get; set; }
public int Score { get; set; }
}
public class Click
{
public string VisitId { get; set; }
public string SearchId { get; set; }
public string ProductCode { get; set; }
}
My problem here is that I expect Count to be one in that specific test, but it just doesn't seem to add the Clicks in the Click map and the result is 0 clicks. I'm totally confused, and I'm sure that there is a really simple solution to my problem, but I just can't find it..
..hope there is a week-end warrior out there who can take me under his wings.
Yes, it was a brain-melt, for me non-trivial, but still. The proper reduce should look like this:
Reduce =
results =>
from result in results
group result by
new { SearchId = result.SearchId, ProductCode = result.ProductCode }
into g
select
new
{
SearchId = g.Key.SearchId,
ProductCode = g.Key.ProductCode,
Score = g.Select(x=>x.Score).FirstOrDefault(),
Clicks = g.Sum(x => x.Clicks)
};
Not all Maps had the Score set to a non-null-value, and therefore my original version had a problem with:
Score = g.First(x => x.Score != null).Score
Mental note, use:
Score = g.Select(x=>x.Score).FirstOrDefault()
Don't use:
Score = g.First(x => x.Score != null).Score

MultiMap / Reduce - Counts = 0?

I want to create an index for a query, I want to return to my view a list of Audio items along with statistics for these items, which are TotalDownloads & TotalPlays.
Here are my relevant docs:
Audio
- Id
- ArtistName
- Name
AudioCounter
- AudioId
- Type
- DateTime
Here is my current Index:
public class AudioWithCounters : AbstractMultiMapIndexCreationTask<AudioWithCounters.AudioViewModel>
{
public class AudioViewModel
{
public string Id { get; set; }
public string ArtistName { get; set; }
public string Name { get; set; }
public int TotalDownloads { get; set; }
public int TotalPlays { get; set; }
}
public AudioWithCounters()
{
AddMap<Audio>(audios => from audio in audios
select new
{
Id = audio.Id,
ArtistName = audio.ArtistName,
Name = audio.Name,
TotalDownloads = 0,
TotalPlays = 0
});
AddMap<AudioCounter>(counters => from counter in counters
where counter.Type == Core.Enums.Audio.AudioCounterType.Download
select new
{
Id = counter.AudioId,
ArtistName = (string)null,
Name = (string)null,
TotalDownloads = 1,
TotalPlays = 0
});
AddMap<AudioCounter>(counters => from counter in counters
where counter.Type == Core.Enums.Audio.AudioCounterType.Download
select new
{
Id = counter.AudioId,
ArtistName = (string)null,
Name = (string)null,
TotalDownloads = 0,
TotalPlays = 1
});
Reduce = results => from result in results
group result by result.Id
into g
select new
{
Id = g.Key,
ArtistName = g.Select(x => x.ArtistName).Where(x => x != null).First(),
Name = g.Select(x => x.Name).Where(x => x != null).First(),
TotalDownloads = g.Sum(x => x.TotalDownloads),
TotalPlays = g.Sum(x => x.TotalPlays)
};
}
}
However, my TotalDownloads & TotalPlays are always 0 even though there should be data in there. What am I doing wrong?
In the reduce function, replace .First() with .FirstOrDefault(), then it works.
Besides that, there is a typo in the second map-function, because you are filtering on the same AudioCounterType.Download.