JavaScript преобразует массив объектов в другой, используя lodash - PullRequest
6 голосов
/ 05 июня 2019

У меня есть массив объектов, который выглядит следующим образом:

[
  {
    type: 'car',
    choices: [
      'audi',
      'honda',
      'bmw',
      'ford'
    ],
  },
  {
    type: 'drink',
    choices: [
      'soda',
      'water',
      'tea',
      'coffee'
    ],
  },
  {
    type: 'food',
    choices: [
      'chips',
      'pizza',
      'cookie',
      'pasta'
    ],
  }
]

Используя lodash, как преобразовать его во что-то похожее на это:

[
  {
    question: [
      {
        drink: "tea"
      },
      {
        car: "bmw"
      }
    ]
  },
  {
    question: [
      {
        food: "cookie"
      },
      {
        car: "ford"
      }
    ]
  },
  {
    question: [
      {
        drink: "soda"
      },
      {
        food: "pizza"
      }
    ]
  },
  {
    question: [
      {
        food: "chips"
      },
      {
        drink: "water"
      }
    ]
  },
  {
    question: [
      {
        car: "audi"
      },
      {
        food: "pasta"
      }
    ]
  },
  {
    question: [
      {
        car: "honda"
      },
      {
        drink: "coffee"
      }
    ]
  },
]

Логика следующая:

  • Каждый вопрос имеет комбинацию из 2 вариантов, где каждый вариант относится к разному типу (например, машина и еда).
  • Сочетание разных типов должно происходить только дважды (машина, еда).
  • Нет дублирования выбора.
  • Выбор должен быть рандомизирован.

Я пытался сгладить массив с помощью этой функции

    let flattenItems = _.flatMap(items, ({ type, choices}) =>
      _.map(choices, choice => ({
        question: [
          { type: type, choice: choice },
          { type: type, choice: choice }
        ],
      })
    ));

но это не то, что мне нужно, и это не случайно. Я не уверен, что мой подход правильный, я думаю, что я должен использовать фильтр или уменьшить

Любая помощь в том, как решить эту проблему, будет полезна при использовании JS или lodash.

Ответы [ 4 ]

2 голосов
/ 05 июня 2019

Использование Lodash

function randomizedQues(items) {
  let result = [];
  let flattenItems = _.flatMap(items, ({ type, choices }) =>
    _.map(choices, choice => ({ type: type, choice: choice })
  ))

  while(flattenItems.length > 1) {
    let r1 = _.random(flattenItems.length - 1),
        e1 = flattenItems[r1];

    let r2 = _.random(flattenItems.length - 1),
        e2 = flattenItems[r2];      

    if(e1.type === e2.type) continue

    result.push({ question: [
        {[e1.type]: e1.choice},
        {[e2.type]: e2.choice}
      ] 
    })
    _.pullAt(flattenItems, [r1, r2])
  }
  return result
}

let items = [{"type":"car","choices":["audi","honda","bmw","ford"]},{"type":"drink","choices":["soda","water","tea","coffee"]},{"type":"food","choices":["chips","pizza","cookie","pasta"]}]

console.log(randomizedQues(items))
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
2 голосов
/ 05 июня 2019

Вы можете получить комбинацию из types и случайного choices селекта с проверкой, если значение уже используется.

function getCombinations(array, size) {

    function c(left, right) {

        function getQuestion({ type, choices }) {
            var random;
            do {
                random = choices[Math.floor(Math.random() * choices.length)];
            } while (taken.get(type).has(random))
            taken.get(type).add(random);
            return { [type]: random };
        }

        left.forEach((v, i, a) => {
            var temp = [...right, v];
            if (temp.length === size) {
                result.push({ question: temp.map(getQuestion) });
            } else {
                c([...a.slice(0, i), ...a.slice(i + 1)], temp);
            }
        });
    }

    var result = [],
        taken = new Map(array.map(({ type }) => [type, new Set]));

    c(array, []);
    return result;
}

var data = [
    { type: 'car', choices: ['audi', 'honda', 'bmw', 'ford'] },
    { type: 'drink', choices: ['soda', 'water', 'tea', 'coffee'] },
    { type: 'food', choices: ['chips', 'pizza', 'cookie', 'pasta'] }
];

console.log(getCombinations(data, 2));
.as-console-wrapper { max-height: 100% !important; top: 0; }
1 голос
/ 05 июня 2019

Вы можете использовать рекурсивную функцию, чтобы продолжать удалять элементы из каждого массива до тех пор, пока у вас не останется достаточно вариантов для заполнения дополнительных вопросов.

Чтобы помочь сделать это, у нас есть функции, которые принимают массив и возвращают случайный элемент плюс массив без этого элемента. Затем мы можем построить вопросы с этими данными, гарантируя, что каждый элемент используется только один раз.

const pipe = (...fns) => x => fns.reduce((v, f) => f(v), x)

const data = [
    { type: 'car', choices: ['audi', 'honda', 'bmw', 'ford'] },
    { type: 'drink', choices: ['soda', 'water', 'tea', 'coffee'] },
    { type: 'food', choices: ['chips', 'pizza', 'cookie', 'pasta'] }
];

const getArrayIndexPair = array => [
  array,
  getRandom(array),
];

const subtractItemFromArray = ([array, index]) => [
  array.slice(index, index + 1)[0],
  [
    ...array.slice(0, index),
    ...array.slice(index + 1, array.length)
  ]
];

const getRandom = array => Math.floor(Math.random()*array.length);
const takeRandom = pipe(
  getArrayIndexPair,
  subtractItemFromArray,
);

const choicesKeyedByType = data
  .reduce((p, c) => ({
    ...p,
    [c.type]: c.choices,
  }), {})

const formQuestions = (choices, questions=[]) => {
  if (Object.keys(choices).length <= 1) {
    return questions;
  }

  const [keyOne, remainingKeys] = takeRandom(Object.keys(choices));
  const [keyTwo] = takeRandom(remainingKeys);
  
  const [choiceOne, remainingKeyOneChoices] = takeRandom(choices[keyOne]);
  const [choiceTwo, remainingKeyTwoChoices] = takeRandom(choices[keyTwo]);

  const newChoices = {
    ...choices,
    [keyOne]: remainingKeyOneChoices,
    [keyTwo]: remainingKeyTwoChoices,
  };
  
  const newChoicesWithoutEmpty = Object.keys(newChoices)
    .filter(key => newChoices[key].length > 0)
    .reduce((p, c) => ({
      ...p,
      [c]: newChoices[c]
    }), {});
    
  const newQuestions = [
    ...questions,
    {
      [keyOne]: choiceOne,
      [keyTwo]: choiceTwo,
    }
  ];
  
  return formQuestions(
    newChoicesWithoutEmpty,
    newQuestions,
  );
};

console.dir(formQuestions(choicesKeyedByType))
1 голос
/ 05 июня 2019

Это было мое мышление, каждая комбинация типов должна появляться дважды.Поэтому я перебрал все массивы и объединил каждый тип с исходящими типами.Затем я зациклился над массивом и соединил каждый тип с предыдущими.В то же время я использовал Math.random(), чтобы выбрать случайный выбор из подмассива choices.Единственная проблема заключается в том, что это не приводит к строгому устранению дубликатов, а полагается на ГСЧ, чтобы гарантировать низкий уровень дубликатов.Вы должны иметь возможность добавлять повторяющийся проверочный код внутри каждого цикла непосредственно перед созданием нового вопроса.

function buildQuestions(data) {
  const questions = []
  for (let i = 0; i < data.length; i++)
    for (let j = i + 1; j < data.length; j++)
      questions.push({question: [{[data[i].type]: data[i].choices[Math.round(Math.random() * (data[i].choices.length - 1))]},
          {[data[j].type]: data[j].choices[Math.round(Math.random() * (data[j].choices.length - 1))]}]})

  for (let i = data.length - 1; i > 0; i--)
    for (let j = i - 1; j >= 0; j--)
      questions.push({question: [{[data[i].type]: data[i].choices[Math.round(Math.random() * (data[i].choices.length - 1))]},
          {[data[j].type]: data[j].choices[Math.round(Math.random() * (data[j].choices.length - 1))]}]})

  return questions
}

const choices = [{ type: 'car',choices: ['audi','honda','bmw','ford'],},{type: 'drink', choices: ['soda','water','tea','coffee'],},{type: 'food',choices: ['chips','pizza','cookie','pasta'],}]

console.log(buildQuestions(choices))
...