javascript - Získajte všetky unikátne kombinácie hodnôt vzhľadom na skupiny
Mám súbor hodnôt atribútov:
Príklad :
[
{
memberAttribute: { attributeName: 'a' },
value: '1'
},
{
memberAttribute: { attributeName: 'a' },
value: '2'
},
{
memberAttribute: { attributeName: 'b' },
value: '1'
},
{
memberAttribute: { attributeName: 'b' },
value: '2'
}
]
Teraz chcem získať všetky jedinečné kombinácie pre dané atribúty člena.
Takže ak by som chcel jedinečné kombinácie pre atribúty člena 'a' a 'b', výsledok by bol:
[
{
memberAttribute: { attributeName: 'a' },
value: '1'
},
{
memberAttribute: { attributeName: 'b' },
value: '1'
}
],
[
{
memberAttribute: { attributeName: 'a' },
value: '1'
},
{
memberAttribute: { attributeName: 'b' },
value: '2'
}
],
[
{
memberAttribute: { attributeName: 'a' },
value: '2'
},
{
memberAttribute: { attributeName: 'b' },
value: '1'
}
],
[
{
memberAttribute: { attributeName: 'a' },
value: '2'
},
{
memberAttribute: { attributeName: 'b' },
value: '2'
}
]
Potrebujem byť schopný zadať n počet vstupných atributov člena, ale želaný výsledok dokážem dosiahnuť iba pre 2 vstupné atribúty.
Súčasné hrozné riešenie:
export const getAttributeCombinations = (
attributes: MemberAttributeValue[]
) => {
// TODO - This algorithm only supports 2 attribute types
// It should support any number of attribute types
const combinations = new Array<Array<MemberAttributeValue>>();
for (const attribute of attributes) {
let unusedAttributes = allExcept(attribute, attributes);
const permutate = () => {
const combination = [attribute];
const toRemove = new Array<Number>();
for (let i = 0; i < unusedAttributes.length; i++) {
const unusedAttribute = unusedAttributes[i];
if (!attributeTypeAlreadyExists(unusedAttribute, combination)) {
toRemove.push(i);
combination.push(unusedAttribute);
}
}
for (const index of toRemove) {
unusedAttributes = remove(index, 1, unusedAttributes);
}
combinations.push(combination);
};
permutate();
while (unusedAttributes.length > 0) {
permutate();
}
}
const sortedCombinations = map(sortByAttributeName, combinations);
return uniqByCombination(sortedCombinations);
};
Príklad zlyhávajúceho testu v Jest:
it('given 3 attribute types should return 12 combinations', () => {
const inclusionAttributes: MemberAttributeValue[] = [
{
memberAttribute: {
attributeName: 'gender',
aliases: [],
contentType: ContentType.String,
type: AttributeType.Mandatory
},
value: 'Male'
},
{
memberAttribute: {
attributeName: 'gender',
aliases: [],
contentType: ContentType.String,
type: AttributeType.Mandatory
},
value: 'Female'
},
{
memberAttribute: {
attributeName: 'age band',
aliases: [],
contentType: ContentType.String,
type: AttributeType.Mandatory
},
value: '0-50'
},
{
memberAttribute: {
attributeName: 'age band',
aliases: [],
contentType: ContentType.String,
type: AttributeType.Mandatory
},
value: '51+'
},
{
memberAttribute: {
attributeName: 'likes',
aliases: [],
contentType: ContentType.String,
type: AttributeType.Mandatory
},
value: 'cats'
},
{
memberAttribute: {
attributeName: 'likes',
aliases: [],
contentType: ContentType.String,
type: AttributeType.Mandatory
},
value: 'dogs'
},
{
memberAttribute: {
attributeName: 'likes',
aliases: [],
contentType: ContentType.String,
type: AttributeType.Mandatory
},
value: 'goats'
}
];
const combinations = getAttributeCombinations(inclusionAttributes);
expect(combinations.length).toBe(12);
for (const combination of combinations) {
expect(combination.length).toBe(3);
}
});
Odpoveď č. 1
To by bolo relatívne jednoduché v Ramda, okrem toho, že funkcia xprod v Ramda funguje iba na dve zoznamy. Ak by fungovala na zozname zoznamov, mohli by sme to urobiť len v niekoľkých krokoch. Ale je veľmi jednoduché napísať si vlastnú:
const xproduct = reduce(pipe(xprod, map(unnest)), [[]])
const transform = pipe(
groupBy(path(['memberAttribute', 'attributeName'])),
values,
xproduct
)
const inclusionAttributes = [
{"memberAttribute": {"attributeName": "gender"}, "value": "Male"},
{"memberAttribute": {"attributeName": "gender"}, "value": "Female"},
{"memberAttribute": {"attributeName": "age band"}, "value": "0-50"},
{"memberAttribute": {"attributeName": "age band"}, "value": "51+"},
{"memberAttribute": {"attributeName": "likes"}, "value": "cats"},
{"memberAttribute": {"attributeName": "likes"}, "value": "dogs"},
{"memberAttribute": {"attributeName": "likes"}, "value": "goats"}
]
console.log(transform(inclusionAttributes))
//=> Male/0-50/cats, Male/0-50/dogs, Male/0-50/goats, Male/51+/cats,...
Môžete to vidieť v akcii na Ramda REPL.
