【问题标题】:Get 3 random, non duplicating objects from an array of objects从对象数组中获取 3 个随机的、非重复的对象
【发布时间】:2020-05-24 15:51:26
【问题描述】:

我正在尝试从一组对象中随机抽取 3 个对象。我的数组来自 Promise。下面是代码。

代码:

var getLocations = admin.firestore().collection('locations')
                getLocations = getLocations.where('area', '==', game_area)
                let locations = new Promise((resolve, reject) => {
                    getLocations.onSnapshot(snapshot => {
                        var results = []
                        snapshot.forEach(doc => {
                            let result = doc.data()
                            result.id = doc.id
                            results.push(result)
                        })
                        return resolve(results)
                    })
                })

                locations.then(() => {
                    // Shuffle locations array and get 3
                    const amountOfLocations = 3
                    const shuffled = locations.sort(() => 0.5 - Math.random())
                    // Get sub-array of first n elements after shuffled
                    let selectedLocations = shuffled.slice(0, amountOfLocations)
                }).catch(err => {
                    console.log(`ERROR IN QUERY: ${JSON.stringify(err)}`);
                })

这是从 Promise 返回的数组的示例。注意:我简化了这个论坛的结果。

locations=[ { name: 'Radio Coffee and Beer',
    timestamp: 1580676902040,
    id: '1KALzdUbf7y3ex2C' },
  { name: 'ZACH Theater',
    timestamp: 1580676946375,
    id: 'Lpxl8xLKCFDKxIhc' },
  { name: 'Alamo Draft House',
    timestamp: 1580676636972,
    id: 'b5F3Tq2y9cD4WQJq' },
  { name: 'Stevie Ray Vaughn Statue',
    timestamp: 1580676764120,
    id: 'bIUl4JU7kUSh6eyi' },
  { timestamp: 1580676967508,
    name: 'The Long Center',
    id: 'xJJOprzYDt3fWqVa' } ]

如您所见,此示例数组有 5 个对象。我想从数组中随机拉出 3 个对象。我认为我的问题来自 Promise 返回数组的方式,但我不确定。

【问题讨论】:

  • console.log(selectedLocations);这是什么结果?
  • @GangadharGandi 它是空的
  • @GangadharGandi 洗牌失败了
  • locations 是这个语法中的一个承诺:locations.sort(...)。您需要从 Promise 中获取价值并通过 locations.then(values => {... const shuffled = values.sort(...); ... }); 使用它
  • @DanielWSrimpel 我有点困惑。你是说我需要做的就是使用locations.then(() => ...使用locations.then(values => ... ?

标签: javascript arrays object promise


【解决方案1】:

您直接在承诺的地点工作。当使用 then 使用 promise 时,我们将得到确切的位置数组。

尝试如下,

locations
  .then((locs) => {
     // Shuffle locations array and get 3
     const amountOfLocations = 3
     const shuffled = locs.sort(() => 0.5 - Math.random())
     // Get sub-array of first n elements after shuffled
     let selectedLocations = shuffled.slice(0, amountOfLocations);
     console.log(selectedLocations); 
  })
  .catch(err => {
     console.log(`ERROR IN QUERY: ${JSON.stringify(err)}`);
  })

【讨论】:

  • 请注意,我的回答中描述的洗牌问题。
【解决方案2】:

正如其他人指出的那样,根本问题是提供给then 的函数没有传递位置信息。下面的代码解决了这个问题。但它也解决了另一个更微妙的问题:

你的洗牌实际上并不是随机的!

An old article An old article Rob Weir 详细解释了为什么这是真的,但这里是对这个想法的简要说明:您的随机播放在对 sort 的调用中使用了许多本质上是硬币翻转的东西,有 50-50 种选择.不管有多少,它选择一个特定结果组合的次数(比如“Alamo Draft House”、“Stevie Ray Vaughn Statue”、“Radio Coffee and Beer”)将显示其价值的一小部分时间必须有一个分母,它是 2 的幂。它可能会出现 1/2 次,或 3/8 次,或 117/1024 次。但它不能出现 1/10 的时间或 1/60 的时间。硬币翻转不能给你那个。三个项目有十种不同的组合(如果顺序很重要,则为 60 种)。所以你不能只用掷硬币来随机洗牌。正如那篇文章所示,这是一个现实世界的问题。

最常见的随机播放方式是使用Fisher-Yates algorithm。下面的代码修改为在选择 n 项目后停止洗牌(您的情况为三个。)

因此,这段代码修复了对 Promise 的回调,并提供了一种 shuffle 技术,可以公平地选择一个子集。

// Dummy implementation for demo
const locations = new Promise((res, rej) => setTimeout(
  () => res([ { name: 'Radio Coffee and Beer', timestamp: 1580676902040, id: '1KALzdUbf7y3ex2C' }, { name: 'ZACH Theater', timestamp: 1580676946375, id: 'Lpxl8xLKCFDKxIhc' }, { name: 'Alamo Draft House', timestamp: 1580676636972, id: 'b5F3Tq2y9cD4WQJq' }, { name: 'Stevie Ray Vaughn Statue', timestamp: 1580676764120, id: 'bIUl4JU7kUSh6eyi' }, { timestamp: 1580676967508, name: 'The Long Center', id: 'xJJOprzYDt3fWqVa' } ]),
  50 // 50 ms delay
))

const randoms = (count) => (xs) => {
  const arr = xs .slice (0), max = arr.length - 1
  for (let i = max; i > max - count; i--) {
    const j = Math .floor (Math .random() * (i + 1));
    [arr[i], arr[j]] = [arr[j], arr[i]];
  }
  return arr .slice (-count)
}          

locations 
  .then ((locs) => randoms (3) (locs))
  .then (console .log)

【讨论】:

    猜你喜欢
    • 2020-12-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-03
    • 1970-01-01
    • 1970-01-01
    • 2017-08-01
    • 2021-06-24
    相关资源
    最近更新 更多