#javascript
Вопрос:
У меня есть этот набор данных:
const data = {
catA: {
color: ['red', 'blue', 'yellow', 'gold'],
animal: ['cat', 'dog', 'hamster'],
},
catB: {
city: ['Syndey', 'Paris', 'Rome', 'London', 'Tokyo', 'Delhi', 'Cairo']
}
}
и то, что я хотел бы получить, — это аналогичная структура данных с процентным значением, присвоенным каждому элементу в массивах. Сумма процентных значений должна составлять 100.
Некоторые элементы могут иметь значение 0, а некоторые нет, не важно, сколько элементов имеют процент 0.
Ниже приведены некоторые примеры того, чего я ожидаю:
const res1 = {
catA: {
color: [['red', 0], ['blue', 10], ['yellow', 5], ['gold', 0]],
animal: [['cat', 50], ['dog', 0], ['hamster', 5]],
},
catB: {
city: [['Syndey', 20], ['Paris', 0], ['Rome', 0], ['London', 10], ['Tokyo', 0], ['Delhi', 0], ['Cairo', 0]]
}
}
const res2 = {
catA: {
color: [['red', 100], ['blue', 0], ['yellow', 0], ['gold', 0]],
animal: [['cat', 0], ['dog', 0], ['hamster', 0]],
},
catB: {
city: [['Syndey', 0], ['Paris', 0], ['Rome', 0], ['London', 0], ['Tokyo', 0], ['Delhi', 0], ['Cairo', 0]]
}
}
const res3 = {
catA: {
color: [['red', 60], ['blue', 0], ['yellow', 0], ['gold', 10]],
animal: [['cat', 5], ['dog', 5], ['hamster', 20]],
},
catB: {
city: [['Syndey', 5], ['Paris', 5], ['Rome', 10], ['London', 5], ['Tokyo', 0], ['Delhi', 5], ['Cairo', 10]]
}
}
Поэтому я создал функцию, которая возвращает массив значений, сумма которых фиксирована:
function randomNumbersWithFixedSum(quantity, sum) {
if (quantity === 1) return [sum];
const randomNum = _.random(0, sum);
return [
randomNum,
...randomNumbersWithFixedSum(quantity - 1, sum - randomNum),
];
}
_.random
является функцией Lodash.
Теперь, как я могу создать функцию, которая возвращает результат, аналогичный предыдущим?
const dataWithPercentages = addPercentages(data, 100)
function addPercentages(dataset, fixedSum) {
const flatData = Object.values(data).flat();
const dataCounter = flatData.length;
const percentages = randomNumbersWithFixedSum(random(1, dataCounter), fixedSum)
// and here?
}
Я не знаю, с чего начать. Может быть, я должен использовать reduce
, но как?
Ответ №1:
Мы могли бы использовать lodash _.range и _.shuffle для этого мы создаем случайные числа с оставшейся суммой, используя Array.map.
Затем мы проходим по каждому пути, который мы хотим задать, уменьшая sum
допустимое значение на каждой итерации.
const data = {
catA: {
color: ['red', 'blue', 'yellow', 'gold'],
animal: ['cat', 'dog', 'hamster'],
},
catB: {
city: ['Sydney', 'Paris', 'Rome', 'London', 'Tokyo', 'Delhi', 'Cairo']
}
}
// Add a last item argument to tell the function to use all the remaining sum input.
function randomNumbersWithFixedSum(input, sum, multiplier, lastItem) {
return _.shuffle(_.range(input.length).map((n, i) => {
const result = (i === input.length-1) amp;amp; lastItem ? sum: Math.floor(Math.random() * (sum))
sum -= resu<
return resu<
})).map((x,i) => [input[i], x * multiplier]);
}
function getArrayPaths(obj, keys = [], paths = []) {
Object.keys(obj).forEach(key => {
if (Array.isArray(obj[key])) {
paths.push([...keys, key].join("."))
} else if (typeof obj === 'object') {
getArrayPaths(obj[key], [...keys, key], paths)
}
})
return paths;
}
let sum = 100;
let paths = getArrayPaths(data);
let result = paths.reduce((acc, path, i) => {
let arr = _.get(acc, path);
let newArr = randomNumbersWithFixedSum(arr, sum, 1, i === paths.length -1);
// Reduce our remaining sum...
sum -= _.sum(newArr.map(a => a[1]));
_.set(acc, path, newArr);
return acc;
}, data);
console.log("Result:", result);
console.log("Remaining sum:", sum);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js" integrity="sha512-WFN04846sdKMIP5LKNphMaWzU7YpMyCU245etK3g/2ARYbPK9Ub18eG ljU96qKRCWh quCY7yefSmlkQw1ANQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
Комментарии:
1. Спасибо, Терри, но это не то, что мне нужно. Мне нужна та же
data
структура2. Ммм нет, обратите внимание, что ваша сумма намного больше 100
Ответ №2:
Вот довольно простой reduce()
способ, который использует рекурсию для прохождения вашего пройденного dataset
. Для каждого свойства, которое является объектом (но не массивом), он рекурсирует, на обратном пути filters()
извлекает свойства, которые являются массивами, суммирует их общую длину и генерирует массив «процентов». Затем он повторяет каждое свойство массива и отключает необходимое количество процентов и помещает их в свойство. splices
function randomNumbersWithFixedSum(quantity, sum) {
let
randArr = Array.from({ length: quantity }, () => Math.random()),
randTotal = randArr.reduce((a, b) => a b, 0),
resultSum = 0,
percent = n => Math.floor((n / randTotal) * sum);
return randArr.map((n, i) => i === quantity - 1 ? sum - resultSum : (resultSum = percent(n), percent(n)));
}
function addPercentages(dataset, fixedSum) {
const zip = (a, b) => a.map((k, i) => [k, b[i]]);
return Object.keys(dataset).reduce((acc, key) => {
let element = dataset[key];
if (typeof element === 'object' amp;amp; !Array.isArray(element)) {
element = addPercentages(element, fixedSum);
const
arrays = Object.entries(element).filter(([, e]) => Array.isArray(e)),
arraysTotalLength = arrays.reduce((a, [, { length }]) => a length, 0),
randomData = randomNumbersWithFixedSum(arraysTotalLength, fixedSum);
for (const [_key, array] of arrays) {
const r = randomData.splice(0, array.length);
element[_key] = zip(array, r);
}
}
return acc[key] = element, acc;
}, {});
}
const
data = { catA: { id: 1, color: ['red', 'blue', 'yellow', 'gold'], animal: ['cat', 'dog', 'hamster'], catC: { id: 3, fish: ['cod', 'halibut', 'monkfish'] } }, catB: { id: 2, city: ['Syndey', 'Paris', 'Rome', 'London', 'Tokyo', 'Delhi', 'Cairo'] } },
result = addPercentages(data, 100);
console.log(JSON.stringify(result, null, 2));
.as-console-wrapper { max-height: 100% !important; top: 0; }
Ответ №3:
это сделает работу за вас. все,что вам нужно сделать, это изменить «Math.floor(Math.random() * 100) 1», который выводит случайное число с вашими желаемыми значениями
function alternate(data) {
return Object.keys(data).reduce((p, c) => ({
...p,
[c]: Object.keys(data[c]).reduce((pr, cu) => ({
...pr,
[cu]: data[c][cu].map(i => ([i, Math.floor(Math.random() * 100) 1]))
})
, {})
})
, {})
}
const data = {
catA: {
color: ['red', 'blue', 'yellow', 'gold'],
animal: ['cat', 'dog', 'hamster'],
},
catB: {
city: ['Syndey', 'Paris', 'Rome', 'London', 'Tokyo', 'Delhi', 'Cairo']
}
}
console.log(alternate(data))
Комментарии:
1. Спасибо, но «общая сумма
data
» должна быть 100