【问题标题】:JavaScript transform array of objects into another using lodashJavaScript 使用 lodash 将对象数组转换为另一个对象
【发布时间】:2019-10-20 21:30:21
【问题描述】:

我有一个如下所示的对象数组:

[
  {
    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 会很好。

【问题讨论】:

  • 是的,但是每两个不同元素的组合应该只发生两次。
  • 例如(汽车和饮料)的组合在整个结果中应该恰好出现两次,顺序无关紧要。
  • 好吧,我想我弄错了。你要求的东西比我最初想象的更复杂。结果中的每个条目都是{ [obj1[randomObj].type] : obj1[randomObj].choices[randomChoice] },并且您正在选择具有相关随机答案的两种类型的对。抱歉,由于某种原因,我最初误读了它,因为您只想要随机类型对的组合。我现在明白了。
  • 这是个好问题。我想问一件事,主要对象将如何选择。您希望它们随机或按顺序排列。我的意思是如果数组中有 4 个元素。然后你想从1-2 然后2-3 然后3-4 然后4-1 然后再1-2 等等中选择。在这种情况下,不会有来自2-4 的组合。你想要那个还是你想要完全随机的..
  • 应该是完全随机的。不是所有的组合都应该存在,只是满足逻辑的组合。

标签: javascript ecmascript-6 lodash


【解决方案1】:

您可以从 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; }

【讨论】:

  • 谢谢。完美运行,我不得不对其进行一些编辑以进行更好的测试。
【解决方案2】:

使用 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>

【讨论】:

  • 谢谢,正是我需要的。
【解决方案3】:

这是我的想法,每种不同的类型组合都需要出现两次。所以我在数组上循环转发,并将每种类型与进行中的类型结合起来。 然后我在数组上向后循环并将每种类型与前面的类型结合起来。同时我使用Math.random()choices 子数组中选择一个随机选项。唯一的问题是这并没有强制执行严格的重复消除,而是依靠 RNG 来保证重复的可能性很低。您应该能够在创建新问题之前在每个循环中添加重复的检查代码。

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))

【讨论】:

    【解决方案4】:

    您可以使用递归函数不断从每个数组中删除项目,直到您没有足够的选项来填写更多问题。

    为了帮助做到这一点,我们有函数接收一个数组,并返回一个随机项,加上没有该项的数组。然后,我们可以使用该数据构建问题,确保每个项目只使用一次。

    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))

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-08-06
      • 2019-04-18
      • 2019-01-26
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多