Change lodash stuff so that it can be tree-shaken out of build (#233)

* Set common package.json sideEffects: false

* Configure SWC to modularize lodash imports

* Import specific lodash functions instead of _

* Add an eslint rule to avoid full lodash import
This commit is contained in:
Marshall Polaris 2022-05-22 01:36:05 -07:00 committed by GitHub
parent 0803a15902
commit 47f10301c8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
93 changed files with 378 additions and 382 deletions

View File

@ -1,4 +1,5 @@
module.exports = {
plugins: ['lodash'],
extends: ['eslint:recommended'],
env: {
browser: true,
@ -14,5 +15,6 @@ module.exports = {
rules: {
'no-unused-vars': 'off',
'no-constant-condition': ['error', { checkLoops: false }],
'lodash/import-scope': [2, 'member'],
},
}

View File

@ -1,3 +1,4 @@
import { range } from 'lodash'
import { Bet, NumericBet } from './bet'
import { getDpmProbability, getValueFromBucket } from './calculate-dpm'
import {
@ -11,7 +12,6 @@ import {
import { User } from './user'
import { LiquidityProvision } from './liquidity-provision'
import { noFees } from './fees'
import * as _ from 'lodash'
export const FIXED_ANTE = 100
@ -127,11 +127,11 @@ export function getNumericAnte(
const betShares = Math.sqrt(ante ** 2 / bucketCount)
const allOutcomeShares = Object.fromEntries(
_.range(0, bucketCount).map((_, i) => [i, betShares])
range(0, bucketCount).map((_, i) => [i, betShares])
)
const allBetAmounts = Object.fromEntries(
_.range(0, bucketCount).map((_, i) => [i, betAnte])
range(0, bucketCount).map((_, i) => [i, betAnte])
)
const anteBet: NumericBet = {

View File

@ -1,4 +1,4 @@
import * as _ from 'lodash'
import { sum, groupBy, mapValues, sumBy } from 'lodash'
import { Binary, CPMM, FullContract } from './contract'
import { CREATOR_FEE, Fees, LIQUIDITY_FEE, noFees, PLATFORM_FEE } from './fees'
@ -278,16 +278,16 @@ export function getCpmmLiquidityPoolWeights(
return liquidity
})
const shareSum = _.sum(liquidityShares)
const shareSum = sum(liquidityShares)
const weights = liquidityShares.map((s, i) => ({
weight: s / shareSum,
providerId: liquidities[i].userId,
}))
const userWeights = _.groupBy(weights, (w) => w.providerId)
const totalUserWeights = _.mapValues(userWeights, (userWeight) =>
_.sumBy(userWeight, (w) => w.weight)
const userWeights = groupBy(weights, (w) => w.providerId)
const totalUserWeights = mapValues(userWeights, (userWeight) =>
sumBy(userWeight, (w) => w.weight)
)
return totalUserWeights
}

View File

@ -1,5 +1,4 @@
import * as _ from 'lodash'
import { cloneDeep, range, sum, sumBy, sortBy, mapValues } from 'lodash'
import { Bet, NumericBet } from './bet'
import {
Binary,
@ -24,7 +23,7 @@ export function getDpmOutcomeProbability(
},
outcome: string
) {
const squareSum = _.sumBy(Object.values(totalShares), (shares) => shares ** 2)
const squareSum = sumBy(Object.values(totalShares), (shares) => shares ** 2)
const shares = totalShares[outcome] ?? 0
return shares ** 2 / squareSum
}
@ -32,8 +31,8 @@ export function getDpmOutcomeProbability(
export function getDpmOutcomeProbabilities(totalShares: {
[outcome: string]: number
}) {
const squareSum = _.sumBy(Object.values(totalShares), (shares) => shares ** 2)
return _.mapValues(totalShares, (shares) => shares ** 2 / squareSum)
const squareSum = sumBy(Object.values(totalShares), (shares) => shares ** 2)
return mapValues(totalShares, (shares) => shares ** 2 / squareSum)
}
export function getNumericBets(
@ -44,20 +43,20 @@ export function getNumericBets(
) {
const { bucketCount } = contract
const bucketNumber = parseInt(bucket)
const buckets = _.range(0, bucketCount)
const buckets = range(0, bucketCount)
const mean = bucketNumber / bucketCount
const allDensities = buckets.map((i) =>
normpdf(i / bucketCount, mean, variance)
)
const densitySum = _.sum(allDensities)
const densitySum = sum(allDensities)
const rawBetAmounts = allDensities
.map((d) => (d / densitySum) * betAmount)
.map((x) => (x >= 1 / bucketCount ? x : 0))
const rawSum = _.sum(rawBetAmounts)
const rawSum = sum(rawBetAmounts)
const scaledBetAmounts = rawBetAmounts.map((x) => (x / rawSum) * betAmount)
const bets = scaledBetAmounts
@ -90,26 +89,24 @@ export const getValueFromBucket = (
export const getExpectedValue = (contract: NumericContract) => {
const { bucketCount, min, max, totalShares } = contract
const totalShareSum = _.sumBy(
const totalShareSum = sumBy(
Object.values(totalShares),
(shares) => shares ** 2
)
const probs = _.range(0, bucketCount).map(
const probs = range(0, bucketCount).map(
(i) => totalShares[i] ** 2 / totalShareSum
)
const values = _.range(0, bucketCount).map(
const values = range(0, bucketCount).map(
(i) =>
// use mid point within bucket
0.5 * (min + (i / bucketCount) * (max - min)) +
0.5 * (min + ((i + 1) / bucketCount) * (max - min))
)
const weightedValues = _.range(0, bucketCount).map(
(i) => probs[i] * values[i]
)
const weightedValues = range(0, bucketCount).map((i) => probs[i] * values[i])
const expectation = _.sum(weightedValues)
const expectation = sum(weightedValues)
const rounded = Math.round(expectation * 1e2) / 1e2
return rounded
}
@ -150,7 +147,7 @@ export function calculateDpmShares(
bet: number,
betChoice: string
) {
const squareSum = _.sumBy(Object.values(totalShares), (shares) => shares ** 2)
const squareSum = sumBy(Object.values(totalShares), (shares) => shares ** 2)
const shares = totalShares[betChoice] ?? 0
const c = 2 * bet * Math.sqrt(squareSum)
@ -166,9 +163,9 @@ export function calculateNumericDpmShares(
) {
const shares: number[] = []
totalShares = _.cloneDeep(totalShares)
totalShares = cloneDeep(totalShares)
const order = _.sortBy(
const order = sortBy(
bets.map(([, amount], i) => [amount, i]),
([amount]) => amount
).map(([, i]) => i)
@ -190,11 +187,11 @@ export function calculateDpmRawShareValue(
betChoice: string
) {
const currentValue = Math.sqrt(
_.sumBy(Object.values(totalShares), (shares) => shares ** 2)
sumBy(Object.values(totalShares), (shares) => shares ** 2)
)
const postSaleValue = Math.sqrt(
_.sumBy(Object.keys(totalShares), (outcome) =>
sumBy(Object.keys(totalShares), (outcome) =>
outcome === betChoice
? Math.max(0, totalShares[outcome] - shares) ** 2
: totalShares[outcome] ** 2
@ -214,12 +211,12 @@ export function calculateDpmMoneyRatio(
const p = getDpmOutcomeProbability(totalShares, outcome)
const actual = _.sum(Object.values(pool)) - shareValue
const actual = sum(Object.values(pool)) - shareValue
const betAmount = p * amount
const expected =
_.sumBy(
sumBy(
Object.keys(totalBets),
(outcome) =>
getDpmOutcomeProbability(totalShares, outcome) *
@ -271,8 +268,8 @@ export function calculateDpmCancelPayout(
bet: Bet
) {
const { totalBets, pool } = contract
const betTotal = _.sum(Object.values(totalBets))
const poolTotal = _.sum(Object.values(pool))
const betTotal = sum(Object.values(totalBets))
const poolTotal = sum(Object.values(pool))
return (bet.amount / betTotal) * poolTotal
}
@ -295,7 +292,7 @@ export function calculateStandardDpmPayout(
const { totalShares, phantomShares, pool } = contract
if (!totalShares[outcome]) return 0
const poolTotal = _.sum(Object.values(pool))
const poolTotal = sum(Object.values(pool))
const total =
totalShares[outcome] - (phantomShares ? phantomShares[outcome] : 0)
@ -356,19 +353,19 @@ function calculateMktDpmPayout(
let probs: { [outcome: string]: number }
if (resolutions) {
const probTotal = _.sum(Object.values(resolutions))
probs = _.mapValues(
const probTotal = sum(Object.values(resolutions))
probs = mapValues(
totalShares,
(_, outcome) => (resolutions[outcome] ?? 0) / probTotal
)
} else {
const squareSum = _.sum(
const squareSum = sum(
Object.values(totalShares).map((shares) => shares ** 2)
)
probs = _.mapValues(totalShares, (shares) => shares ** 2 / squareSum)
probs = mapValues(totalShares, (shares) => shares ** 2 / squareSum)
}
const weightedShareTotal = _.sumBy(Object.keys(totalShares), (outcome) => {
const weightedShareTotal = sumBy(Object.keys(totalShares), (outcome) => {
return probs[outcome] * totalShares[outcome]
})
@ -376,7 +373,7 @@ function calculateMktDpmPayout(
const poolFrac =
outcomeType === 'NUMERIC'
? _.sumBy(
? sumBy(
Object.keys((bet as NumericBet).allOutcomeShares ?? {}),
(outcome) => {
return (
@ -387,7 +384,7 @@ function calculateMktDpmPayout(
)
: (probs[outcome] * shares) / weightedShareTotal
const totalPool = _.sum(Object.values(pool))
const totalPool = sum(Object.values(pool))
const winnings = poolFrac * totalPool
return deductDpmFees(amount, winnings)
}

View File

@ -1,4 +1,4 @@
import * as _ from 'lodash'
import { maxBy } from 'lodash'
import { Bet } from './bet'
import {
calculateCpmmSale,
@ -180,7 +180,7 @@ export function getContractBetNullMetrics() {
export function getTopAnswer(contract: FreeResponseContract) {
const { answers } = contract
const top = _.maxBy(
const top = maxBy(
answers?.map((answer) => ({
answer,
prob: getOutcomeProbability(contract, answer.id),

View File

@ -1,4 +1,3 @@
import * as _ from 'lodash'
import { Answer } from './answer'
import { Fees } from './fees'

View File

@ -1,4 +1,4 @@
import * as _ from 'lodash'
import { sumBy } from 'lodash'
import { Bet, MAX_LOAN_PER_CONTRACT, NumericBet } from './bet'
import {
@ -210,7 +210,7 @@ export const getNumericBetsInfo = (
export const getLoanAmount = (yourBets: Bet[], newBetAmount: number) => {
const openBets = yourBets.filter((bet) => !bet.isSold && !bet.sale)
const prevLoanAmount = _.sumBy(openBets, (bet) => bet.loanAmount ?? 0)
const prevLoanAmount = sumBy(openBets, (bet) => bet.loanAmount ?? 0)
const loanAmount = Math.min(
newBetAmount,
MAX_LOAN_PER_CONTRACT - prevLoanAmount

View File

@ -1,5 +1,4 @@
import * as _ from 'lodash'
import { range } from 'lodash'
import { PHANTOM_ANTE } from './antes'
import {
Binary,
@ -131,7 +130,7 @@ const getNumericProps = (
min: number,
max: number
) => {
const buckets = _.range(0, bucketCount).map((i) => i.toString())
const buckets = range(0, bucketCount).map((i) => i.toString())
const betAnte = ante / bucketCount
const pool = Object.fromEntries(buckets.map((answer) => [answer, betAnte]))

View File

@ -3,6 +3,7 @@
"version": "1.0.0",
"private": true,
"scripts": {},
"sideEffects": false,
"dependencies": {
"lodash": "4.17.21"
},

View File

@ -1,4 +1,4 @@
import * as _ from 'lodash'
import { sum, groupBy, sumBy, mapValues } from 'lodash'
import { Bet, NumericBet } from './bet'
import { deductDpmFees, getDpmProbability } from './calculate-dpm'
@ -17,10 +17,10 @@ export const getDpmCancelPayouts = (
bets: Bet[]
) => {
const { pool } = contract
const poolTotal = _.sum(Object.values(pool))
const poolTotal = sum(Object.values(pool))
console.log('resolved N/A, pool M$', poolTotal)
const betSum = _.sumBy(bets, (b) => b.amount)
const betSum = sumBy(bets, (b) => b.amount)
const payouts = bets.map((bet) => ({
userId: bet.userId,
@ -42,8 +42,8 @@ export const getDpmStandardPayouts = (
) => {
const winningBets = bets.filter((bet) => bet.outcome === outcome)
const poolTotal = _.sum(Object.values(contract.pool))
const totalShares = _.sumBy(winningBets, (b) => b.shares)
const poolTotal = sum(Object.values(contract.pool))
const totalShares = sumBy(winningBets, (b) => b.shares)
const payouts = winningBets.map(({ userId, amount, shares }) => {
const winnings = (shares / totalShares) * poolTotal
@ -54,7 +54,7 @@ export const getDpmStandardPayouts = (
return { userId, profit, payout }
})
const profits = _.sumBy(payouts, (po) => Math.max(0, po.profit))
const profits = sumBy(payouts, (po) => Math.max(0, po.profit))
const creatorFee = DPM_CREATOR_FEE * profits
const platformFee = DPM_PLATFORM_FEE * profits
@ -93,10 +93,10 @@ export const getNumericDpmPayouts = (
contract: FullContract<DPM, any>,
bets: NumericBet[]
) => {
const totalShares = _.sumBy(bets, (bet) => bet.allOutcomeShares[outcome] ?? 0)
const totalShares = sumBy(bets, (bet) => bet.allOutcomeShares[outcome] ?? 0)
const winningBets = bets.filter((bet) => !!bet.allOutcomeShares[outcome])
const poolTotal = _.sum(Object.values(contract.pool))
const poolTotal = sum(Object.values(contract.pool))
const payouts = winningBets.map(
({ userId, allBetAmounts, allOutcomeShares }) => {
@ -112,7 +112,7 @@ export const getNumericDpmPayouts = (
}
)
const profits = _.sumBy(payouts, (po) => Math.max(0, po.profit))
const profits = sumBy(payouts, (po) => Math.max(0, po.profit))
const creatorFee = DPM_CREATOR_FEE * profits
const platformFee = DPM_PLATFORM_FEE * profits
@ -156,7 +156,7 @@ export const getDpmMktPayouts = (
? getDpmProbability(contract.totalShares)
: resolutionProbability
const weightedShareTotal = _.sumBy(bets, (b) =>
const weightedShareTotal = sumBy(bets, (b) =>
b.outcome === 'YES' ? p * b.shares : (1 - p) * b.shares
)
@ -170,7 +170,7 @@ export const getDpmMktPayouts = (
return { userId, profit, payout }
})
const profits = _.sumBy(payouts, (po) => Math.max(0, po.profit))
const profits = sumBy(payouts, (po) => Math.max(0, po.profit))
const creatorFee = DPM_CREATOR_FEE * profits
const platformFee = DPM_PLATFORM_FEE * profits
@ -210,15 +210,15 @@ export const getPayoutsMultiOutcome = (
contract: FullContract<DPM, Multi | FreeResponse>,
bets: Bet[]
) => {
const poolTotal = _.sum(Object.values(contract.pool))
const poolTotal = sum(Object.values(contract.pool))
const winningBets = bets.filter((bet) => resolutions[bet.outcome])
const betsByOutcome = _.groupBy(winningBets, (bet) => bet.outcome)
const sharesByOutcome = _.mapValues(betsByOutcome, (bets) =>
_.sumBy(bets, (bet) => bet.shares)
const betsByOutcome = groupBy(winningBets, (bet) => bet.outcome)
const sharesByOutcome = mapValues(betsByOutcome, (bets) =>
sumBy(bets, (bet) => bet.shares)
)
const probTotal = _.sum(Object.values(resolutions))
const probTotal = sum(Object.values(resolutions))
const payouts = winningBets.map(({ userId, outcome, amount, shares }) => {
const prob = resolutions[outcome] / probTotal
@ -229,7 +229,7 @@ export const getPayoutsMultiOutcome = (
return { userId, profit, payout }
})
const profits = _.sumBy(payouts, (po) => po.profit)
const profits = sumBy(payouts, (po) => po.profit)
const creatorFee = DPM_CREATOR_FEE * profits
const platformFee = DPM_PLATFORM_FEE * profits

View File

@ -1,4 +1,4 @@
import * as _ from 'lodash'
import { sum } from 'lodash'
import { Bet } from './bet'
import { getProbability } from './calculate'
@ -50,7 +50,7 @@ export const getStandardFixedPayouts = (
'pool',
contract.pool[outcome],
'payouts',
_.sum(payouts),
sum(payouts),
'creator fee',
creatorPayout
)
@ -105,7 +105,7 @@ export const getMktFixedPayouts = (
'pool',
p * contract.pool.YES + (1 - p) * contract.pool.NO,
'payouts',
_.sum(payouts),
sum(payouts),
'creator fee',
creatorPayout
)

View File

@ -1,4 +1,4 @@
import * as _ from 'lodash'
import { sumBy, groupBy, mapValues } from 'lodash'
import { Bet, NumericBet } from './bet'
import {
@ -32,16 +32,19 @@ export type Payout = {
export const getLoanPayouts = (bets: Bet[]): Payout[] => {
const betsWithLoans = bets.filter((bet) => bet.loanAmount)
const betsByUser = _.groupBy(betsWithLoans, (bet) => bet.userId)
const loansByUser = _.mapValues(betsByUser, (bets) =>
_.sumBy(bets, (bet) => -(bet.loanAmount ?? 0))
const betsByUser = groupBy(betsWithLoans, (bet) => bet.userId)
const loansByUser = mapValues(betsByUser, (bets) =>
sumBy(bets, (bet) => -(bet.loanAmount ?? 0))
)
return _.toPairs(loansByUser).map(([userId, payout]) => ({ userId, payout }))
return Object.entries(loansByUser).map(([userId, payout]) => ({
userId,
payout,
}))
}
export const groupPayoutsByUser = (payouts: Payout[]) => {
const groups = _.groupBy(payouts, (payout) => payout.userId)
return _.mapValues(groups, (group) => _.sumBy(group, (g) => g.payout))
const groups = groupBy(payouts, (payout) => payout.userId)
return mapValues(groups, (group) => sumBy(group, (g) => g.payout))
}
export type PayoutInfo = {

View File

@ -1,4 +1,4 @@
import * as _ from 'lodash'
import { union, sum, sumBy, sortBy, groupBy, mapValues } from 'lodash'
import { Bet } from './bet'
import { Contract } from './contract'
import { ClickEvent } from './tracking'
@ -21,13 +21,13 @@ export const getRecommendedContracts = (
const yourWordFrequency = contractsToWordFrequency(yourContracts)
const otherWordFrequency = contractsToWordFrequency(notYourContracts)
const words = _.union(
const words = union(
Object.keys(yourWordFrequency),
Object.keys(otherWordFrequency)
)
const yourWeightedFrequency = _.fromPairs(
_.map(words, (word) => {
const yourWeightedFrequency = Object.fromEntries(
words.map((word) => {
const [yourFreq, otherFreq] = [
yourWordFrequency[word] ?? 0,
otherWordFrequency[word] ?? 0,
@ -47,7 +47,7 @@ export const getRecommendedContracts = (
const scoredContracts = contracts.map((contract) => {
const wordFrequency = contractToWordFrequency(contract)
const score = _.sumBy(Object.keys(wordFrequency), (word) => {
const score = sumBy(Object.keys(wordFrequency), (word) => {
const wordFreq = wordFrequency[word] ?? 0
const weight = yourWeightedFrequency[word] ?? 0
return wordFreq * weight
@ -59,7 +59,7 @@ export const getRecommendedContracts = (
}
})
return _.sortBy(scoredContracts, (scored) => -scored.score).map(
return sortBy(scoredContracts, (scored) => -scored.score).map(
(scored) => scored.contract
)
}
@ -87,8 +87,8 @@ const getWordsCount = (text: string) => {
}
const toFrequency = (counts: { [word: string]: number }) => {
const total = _.sum(Object.values(counts))
return _.mapValues(counts, (count) => count / total)
const total = sum(Object.values(counts))
return mapValues(counts, (count) => count / total)
}
const contractToWordFrequency = (contract: Contract) =>
@ -108,8 +108,8 @@ export const getWordScores = (
clicks: ClickEvent[],
bets: Bet[]
) => {
const contractClicks = _.groupBy(clicks, (click) => click.contractId)
const contractBets = _.groupBy(bets, (bet) => bet.contractId)
const contractClicks = groupBy(clicks, (click) => click.contractId)
const contractBets = groupBy(bets, (bet) => bet.contractId)
const yourContracts = contracts.filter(
(c) =>
@ -117,25 +117,22 @@ export const getWordScores = (
)
const yourTfIdf = calculateContractTfIdf(yourContracts)
const contractWordScores = _.mapValues(
yourTfIdf,
(wordsTfIdf, contractId) => {
const viewCount = contractViewCounts[contractId] ?? 0
const clickCount = contractClicks[contractId]?.length ?? 0
const betCount = contractBets[contractId]?.length ?? 0
const contractWordScores = mapValues(yourTfIdf, (wordsTfIdf, contractId) => {
const viewCount = contractViewCounts[contractId] ?? 0
const clickCount = contractClicks[contractId]?.length ?? 0
const betCount = contractBets[contractId]?.length ?? 0
const factor =
-1 * Math.log(viewCount + 1) +
10 * Math.log(betCount + clickCount / 4 + 1)
const factor =
-1 * Math.log(viewCount + 1) +
10 * Math.log(betCount + clickCount / 4 + 1)
return _.mapValues(wordsTfIdf, (tfIdf) => tfIdf * factor)
}
)
return mapValues(wordsTfIdf, (tfIdf) => tfIdf * factor)
})
const wordScores = Object.values(contractWordScores).reduce(addObjects, {})
const minScore = Math.min(...Object.values(wordScores))
const maxScore = Math.max(...Object.values(wordScores))
const normalizedWordScores = _.mapValues(
const normalizedWordScores = mapValues(
wordScores,
(score) => (score - minScore) / (maxScore - minScore)
)
@ -156,7 +153,7 @@ export function getContractScore(
if (Object.keys(wordScores).length === 0) return 1
const wordFrequency = contractToWordFrequency(contract)
const score = _.sumBy(Object.keys(wordFrequency), (word) => {
const score = sumBy(Object.keys(wordFrequency), (word) => {
const wordFreq = wordFrequency[word] ?? 0
const weight = wordScores[word] ?? 0
return wordFreq * weight
@ -178,11 +175,13 @@ function calculateContractTfIdf(contracts: Contract[]) {
}
}
const wordIdf = _.mapValues(wordsCount, (count) =>
const wordIdf = mapValues(wordsCount, (count) =>
Math.log(contracts.length / count)
)
const contractWordsTfIdf = _.map(contractFreq, (wordFreq) =>
_.mapValues(wordFreq, (freq, word) => freq * wordIdf[word])
const contractWordsTfIdf = contractFreq.map((wordFreq) =>
mapValues(wordFreq, (freq, word) => freq * wordIdf[word])
)
return Object.fromEntries(
contracts.map((c, i) => [c.id, contractWordsTfIdf[i]])
)
return _.fromPairs(contracts.map((c, i) => [c.id, contractWordsTfIdf[i]]))
}

View File

@ -1,13 +1,13 @@
import * as _ from 'lodash'
import { groupBy, sumBy, mapValues, partition } from 'lodash'
import { Bet } from './bet'
import { Binary, Contract, FullContract } from './contract'
import { getPayouts } from './payouts'
export function scoreCreators(contracts: Contract[], bets: Bet[][]) {
const creatorScore = _.mapValues(
_.groupBy(contracts, ({ creatorId }) => creatorId),
(contracts) => _.sumBy(contracts, ({ pool }) => pool.YES + pool.NO)
const creatorScore = mapValues(
groupBy(contracts, ({ creatorId }) => creatorId),
(contracts) => sumBy(contracts, ({ pool }) => pool.YES + pool.NO)
)
return creatorScore
@ -30,7 +30,7 @@ export function scoreUsersByContract(
) {
const { resolution, resolutionProbability } = contract
const [closedBets, openBets] = _.partition(
const [closedBets, openBets] = partition(
bets,
(bet) => bet.isSold || bet.sale
)
@ -58,9 +58,9 @@ export function scoreUsersByContract(
const netPayouts = [...resolvePayouts, ...salePayouts, ...investments]
const userScore = _.mapValues(
_.groupBy(netPayouts, (payout) => payout.userId),
(payouts) => _.sumBy(payouts, ({ payout }) => payout)
const userScore = mapValues(
groupBy(netPayouts, (payout) => payout.userId),
(payouts) => sumBy(payouts, ({ payout }) => payout)
)
return userScore

View File

@ -1,4 +1,4 @@
import * as _ from 'lodash'
import { union } from 'lodash'
export const removeUndefinedProps = <T>(obj: T): T => {
let newObj: any = {}
@ -14,7 +14,7 @@ export const addObjects = <T extends { [key: string]: number }>(
obj1: T,
obj2: T
) => {
const keys = _.union(Object.keys(obj1), Object.keys(obj2))
const keys = union(Object.keys(obj1), Object.keys(obj2))
const newObj = {} as any
for (let key of keys) {

View File

@ -1,4 +1,5 @@
module.exports = {
plugins: ['lodash'],
extends: ['eslint:recommended'],
ignorePatterns: ['lib'],
env: {
@ -17,5 +18,6 @@ module.exports = {
rules: {
'no-unused-vars': 'off',
'no-constant-condition': ['error', { checkLoops: false }],
'lodash/import-scope': [2, 'member'],
},
}

View File

@ -1,6 +1,5 @@
import * as functions from 'firebase-functions'
import * as admin from 'firebase-admin'
import * as _ from 'lodash'
import { getUser } from './utils'
import { Contract } from '../../common/contract'
@ -34,7 +33,7 @@ export const createFold = functions.runWith({ minInstances: 1 }).https.onCall(
return { status: 'error', message: 'About must be a string' }
about = about.trim().slice(0, 140)
if (!_.isArray(tags))
if (!Array.isArray(tags))
return { status: 'error', message: 'Tags must be an array of strings' }
console.log(

View File

@ -1,5 +1,3 @@
import * as _ from 'lodash'
import { DOMAIN, PROJECT_ID } from '../../common/envs/constants'
import { Answer } from '../../common/answer'
import { Bet } from '../../common/bet'

View File

@ -1,6 +1,5 @@
import * as functions from 'firebase-functions'
import * as admin from 'firebase-admin'
import * as _ from 'lodash'
import { getContract } from './utils'
import { Bet } from '../../common/bet'

View File

@ -1,6 +1,6 @@
import * as functions from 'firebase-functions'
import * as admin from 'firebase-admin'
import * as _ from 'lodash'
import { uniq } from 'lodash'
import { getContract, getUser, getValues } from './utils'
import { Comment } from '../../common/comment'
@ -60,7 +60,7 @@ export const onCreateComment = functions.firestore
firestore.collection('contracts').doc(contractId).collection('comments')
)
const recipientUserIds = _.uniq([
const recipientUserIds = uniq([
contract.creatorId,
...comments.map((comment) => comment.userId),
]).filter((id) => id !== comment.userId)

View File

@ -1,5 +1,5 @@
import * as admin from 'firebase-admin'
import * as _ from 'lodash'
import { partition, sumBy } from 'lodash'
import { Bet } from '../../common/bet'
import { getProbability } from '../../common/calculate'
@ -25,14 +25,14 @@ export const redeemShares = async (userId: string, contractId: string) => {
.where('userId', '==', userId)
)
const bets = betsSnap.docs.map((doc) => doc.data() as Bet)
const [yesBets, noBets] = _.partition(bets, (b) => b.outcome === 'YES')
const yesShares = _.sumBy(yesBets, (b) => b.shares)
const noShares = _.sumBy(noBets, (b) => b.shares)
const [yesBets, noBets] = partition(bets, (b) => b.outcome === 'YES')
const yesShares = sumBy(yesBets, (b) => b.shares)
const noShares = sumBy(noBets, (b) => b.shares)
const amount = Math.min(yesShares, noShares)
if (amount <= 0) return
const prevLoanAmount = _.sumBy(bets, (bet) => bet.loanAmount ?? 0)
const prevLoanAmount = sumBy(bets, (bet) => bet.loanAmount ?? 0)
const loanPaid = Math.min(prevLoanAmount, amount)
const netAmount = amount - loanPaid

View File

@ -1,6 +1,6 @@
import * as functions from 'firebase-functions'
import * as admin from 'firebase-admin'
import * as _ from 'lodash'
import { difference, uniq, mapValues, groupBy, sumBy } from 'lodash'
import { Contract } from '../../common/contract'
import { User } from '../../common/user'
@ -187,13 +187,13 @@ const sendResolutionEmails = async (
resolutionProbability?: number,
resolutions?: { [outcome: string]: number }
) => {
const nonWinners = _.difference(
_.uniq(openBets.map(({ userId }) => userId)),
const nonWinners = difference(
uniq(openBets.map(({ userId }) => userId)),
Object.keys(userPayouts)
)
const investedByUser = _.mapValues(
_.groupBy(openBets, (bet) => bet.userId),
(bets) => _.sumBy(bets, (bet) => bet.amount)
const investedByUser = mapValues(
groupBy(openBets, (bet) => bet.userId),
(bets) => sumBy(bets, (bet) => bet.amount)
)
const emailPayouts = [
...Object.entries(userPayouts),

View File

@ -1,5 +1,4 @@
import * as admin from 'firebase-admin'
import * as _ from 'lodash'
import { initAdmin } from './script-init'
initAdmin()

View File

@ -1,5 +1,4 @@
import * as admin from 'firebase-admin'
import * as _ from 'lodash'
import { initAdmin } from './script-init'
initAdmin()

View File

@ -1,5 +1,5 @@
import * as admin from 'firebase-admin'
import * as _ from 'lodash'
import { sortBy } from 'lodash'
import { initAdmin } from './script-init'
initAdmin()
@ -20,7 +20,7 @@ async function migrateContract(
.get()
.then((snap) => snap.docs.map((bet) => bet.data() as Bet))
const lastBet = _.sortBy(bets, (bet) => -bet.createdTime)[0]
const lastBet = sortBy(bets, (bet) => -bet.createdTime)[0]
if (lastBet) {
const probAfter = getDpmProbability(contract.totalShares)

View File

@ -1,5 +1,4 @@
import * as admin from 'firebase-admin'
import * as _ from 'lodash'
import { initAdmin } from './script-init'
initAdmin()

View File

@ -1,5 +1,4 @@
import * as admin from 'firebase-admin'
import * as _ from 'lodash'
import * as fs from 'fs'
import { initAdmin } from './script-init'

View File

@ -1,5 +1,5 @@
import * as admin from 'firebase-admin'
import * as _ from 'lodash'
import { uniq } from 'lodash'
import { initAdmin } from './script-init'
initAdmin()
@ -19,7 +19,7 @@ async function lowercaseFoldTags() {
const foldRef = firestore.doc(`folds/${fold.id}`)
const { tags } = fold
const lowercaseTags = _.uniq(tags.map((tag) => tag.toLowerCase()))
const lowercaseTags = uniq(tags.map((tag) => tag.toLowerCase()))
console.log('Adding lowercase tags', fold.slug, lowercaseTags)

View File

@ -1,5 +1,4 @@
import * as admin from 'firebase-admin'
import * as _ from 'lodash'
import { initAdmin } from './script-init'
initAdmin()

View File

@ -1,5 +1,5 @@
import * as admin from 'firebase-admin'
import * as _ from 'lodash'
import { sumBy } from 'lodash'
import { initAdmin } from './script-init'
initAdmin()
@ -25,8 +25,8 @@ async function migrateContract(contractRef: DocRef, contract: Contract) {
.then((snap) => snap.docs.map((bet) => bet.data() as Bet))
const totalShares = {
YES: _.sumBy(bets, (bet) => (bet.outcome === 'YES' ? bet.shares : 0)),
NO: _.sumBy(bets, (bet) => (bet.outcome === 'NO' ? bet.shares : 0)),
YES: sumBy(bets, (bet) => (bet.outcome === 'YES' ? bet.shares : 0)),
NO: sumBy(bets, (bet) => (bet.outcome === 'NO' ? bet.shares : 0)),
}
await contractRef.update({ totalShares })

View File

@ -1,5 +1,5 @@
import * as admin from 'firebase-admin'
import * as _ from 'lodash'
import { sortBy } from 'lodash'
import { initAdmin } from './script-init'
initAdmin()
@ -48,7 +48,7 @@ async function recalculateContract(contractRef: DocRef, isCommit = false) {
const betsRef = contractRef.collection('bets')
const betDocs = await transaction.get(betsRef)
const bets = _.sortBy(
const bets = sortBy(
betDocs.docs.map((d) => d.data() as Bet),
(b) => b.createdTime
)

View File

@ -1,5 +1,5 @@
import * as admin from 'firebase-admin'
import * as _ from 'lodash'
import { sortBy, sumBy } from 'lodash'
import { initAdmin } from './script-init'
initAdmin()
@ -35,7 +35,7 @@ async function recalculateContract(
const contract = contractDoc.data() as FullContract<DPM, Binary>
const betDocs = await transaction.get(contractRef.collection('bets'))
const bets = _.sortBy(
const bets = sortBy(
betDocs.docs.map((d) => d.data() as Bet),
(b) => b.createdTime
)
@ -43,8 +43,8 @@ async function recalculateContract(
const phantomAnte = startPool.YES + startPool.NO
const leftovers =
_.sumBy(bets, (b) => b.amount) -
_.sumBy(bets, (b) => {
sumBy(bets, (b) => b.amount) -
sumBy(bets, (b) => {
if (!b.sale) return b.amount
const soldBet = bets.find((bet) => bet.id === b.sale?.betId)
return soldBet?.amount || 0

View File

@ -1,5 +1,5 @@
import * as admin from 'firebase-admin'
import * as _ from 'lodash'
import { flatten, groupBy, sumBy, mapValues } from 'lodash'
import { initAdmin } from './script-init'
initAdmin()
@ -35,12 +35,12 @@ async function checkIfPayOutAgain(contractRef: DocRef, contract: Contract) {
)
const loanPayouts = getLoanPayouts(openBets)
const groups = _.groupBy(
const groups = groupBy(
[...payouts, ...loanPayouts],
(payout) => payout.userId
)
const userPayouts = _.mapValues(groups, (group) =>
_.sumBy(group, (g) => g.payout)
const userPayouts = mapValues(groups, (group) =>
sumBy(group, (g) => g.payout)
)
const entries = Object.entries(userPayouts)
@ -93,7 +93,7 @@ async function payOutContractAgain() {
)
)
const flattened = _.flatten(toPayOutAgain.map((d) => d.toBePaidOut))
const flattened = flatten(toPayOutAgain.map((d) => d.toBePaidOut))
for (const [userId, payout] of flattened) {
console.log('Paying out', userId, payout)

View File

@ -1,5 +1,5 @@
import * as admin from 'firebase-admin'
import * as _ from 'lodash'
import { sumBy } from 'lodash'
import { initAdmin } from './script-init'
initAdmin()
@ -20,13 +20,13 @@ async function recalculateContract(contractRef: DocRef, contract: Contract) {
const openBets = bets.filter((b) => !b.isSold && !b.sale)
const totalShares = {
YES: _.sumBy(openBets, (bet) => (bet.outcome === 'YES' ? bet.shares : 0)),
NO: _.sumBy(openBets, (bet) => (bet.outcome === 'NO' ? bet.shares : 0)),
YES: sumBy(openBets, (bet) => (bet.outcome === 'YES' ? bet.shares : 0)),
NO: sumBy(openBets, (bet) => (bet.outcome === 'NO' ? bet.shares : 0)),
}
const totalBets = {
YES: _.sumBy(openBets, (bet) => (bet.outcome === 'YES' ? bet.amount : 0)),
NO: _.sumBy(openBets, (bet) => (bet.outcome === 'NO' ? bet.amount : 0)),
YES: sumBy(openBets, (bet) => (bet.outcome === 'YES' ? bet.amount : 0)),
NO: sumBy(openBets, (bet) => (bet.outcome === 'NO' ? bet.amount : 0)),
}
await contractRef.update({ totalShares, totalBets })

View File

@ -1,5 +1,4 @@
import * as admin from 'firebase-admin'
import * as _ from 'lodash'
import { initAdmin } from './script-init'
initAdmin()

View File

@ -1,5 +1,4 @@
import * as admin from 'firebase-admin'
import * as _ from 'lodash'
import { initAdmin } from './script-init'
initAdmin()

View File

@ -1,5 +1,5 @@
import * as admin from 'firebase-admin'
import * as _ from 'lodash'
import { uniq } from 'lodash'
import { initAdmin } from './script-init'
initAdmin()
@ -19,7 +19,7 @@ async function updateContractTags() {
for (const contract of contracts) {
const contractRef = firestore.doc(`contracts/${contract.id}`)
const tags = _.uniq([
const tags = uniq([
...parseTags(contract.question + contract.description),
...(contract.tags ?? []),
])

View File

@ -1,5 +1,4 @@
import * as admin from 'firebase-admin'
import * as _ from 'lodash'
import { initAdmin } from './script-init'
initAdmin()

View File

@ -1,5 +1,4 @@
import * as admin from 'firebase-admin'
import * as _ from 'lodash'
import { initAdmin } from './script-init'
initAdmin()

View File

@ -1,4 +1,4 @@
import * as _ from 'lodash'
import { partition, sumBy } from 'lodash'
import * as admin from 'firebase-admin'
import * as functions from 'firebase-functions'
@ -51,15 +51,15 @@ export const sellShares = functions.runWith({ minInstances: 1 }).https.onCall(
contractDoc.collection('bets').where('userId', '==', userId)
)
const prevLoanAmount = _.sumBy(userBets, (bet) => bet.loanAmount ?? 0)
const prevLoanAmount = sumBy(userBets, (bet) => bet.loanAmount ?? 0)
const [yesBets, noBets] = _.partition(
const [yesBets, noBets] = partition(
userBets ?? [],
(bet) => bet.outcome === 'YES'
)
const [yesShares, noShares] = [
_.sumBy(yesBets, (bet) => bet.shares),
_.sumBy(noBets, (bet) => bet.shares),
sumBy(yesBets, (bet) => bet.shares),
sumBy(noBets, (bet) => bet.shares),
]
const maxShares = outcome === 'YES' ? yesShares : noShares

View File

@ -1,6 +1,5 @@
import * as functions from 'firebase-functions'
import * as admin from 'firebase-admin'
import * as _ from 'lodash'
import { getUser } from './utils'
import { PrivateUser } from '../../common/user'

View File

@ -1,6 +1,6 @@
import * as functions from 'firebase-functions'
import * as admin from 'firebase-admin'
import * as _ from 'lodash'
import { sumBy } from 'lodash'
import { getValues } from './utils'
import { Contract } from '../../common/contract'
@ -39,5 +39,5 @@ const computeVolumeFrom = async (contract: Contract, timeAgoMs: number) => {
.where('createdTime', '>', Date.now() - timeAgoMs)
)
return _.sumBy(bets, (bet) => (bet.isRedemption ? 0 : Math.abs(bet.amount)))
return sumBy(bets, (bet) => (bet.isRedemption ? 0 : Math.abs(bet.amount)))
}

View File

@ -1,6 +1,6 @@
import * as _ from 'lodash'
import * as functions from 'firebase-functions'
import * as admin from 'firebase-admin'
import { shuffle, sortBy } from 'lodash'
import { getValue, getValues } from './utils'
import { Contract } from '../../common/contract'
@ -30,7 +30,7 @@ const BATCH_SIZE = 30
const MAX_BATCHES = 50
const getUserBatches = async () => {
const users = _.shuffle(await getValues<User>(firestore.collection('users')))
const users = shuffle(await getValues<User>(firestore.collection('users')))
let userBatches: User[][] = []
for (let i = 0; i < users.length; i += BATCH_SIZE) {
userBatches.push(users.slice(i, i + BATCH_SIZE))
@ -128,7 +128,7 @@ export const computeFeed = async (user: User, contracts: Contract[]) => {
return [contract, score] as [Contract, number]
})
const sortedContracts = _.sortBy(
const sortedContracts = sortBy(
scoredContracts,
([_, score]) => score
).reverse()

View File

@ -1,6 +1,5 @@
import * as functions from 'firebase-functions'
import * as admin from 'firebase-admin'
import * as _ from 'lodash'
import { getValue, getValues } from './utils'
import { Contract } from '../../common/contract'

View File

@ -1,6 +1,6 @@
import * as functions from 'firebase-functions'
import * as admin from 'firebase-admin'
import * as _ from 'lodash'
import { sum, sumBy } from 'lodash'
import { getValues } from './utils'
import { Contract } from '../../common/contract'
@ -19,7 +19,7 @@ export const updateUserMetrics = functions.pubsub
getValues<Contract>(firestore.collection('contracts')),
])
const contractsDict = _.fromPairs(
const contractsDict = Object.fromEntries(
contracts.map((contract) => [contract.id, contract])
)
@ -43,12 +43,12 @@ export const updateUserMetrics = functions.pubsub
const computeInvestmentValue = async (
user: User,
contractsDict: _.Dictionary<Contract>
contractsDict: { [k: string]: Contract }
) => {
const query = firestore.collectionGroup('bets').where('userId', '==', user.id)
const bets = await getValues<Bet>(query)
return _.sumBy(bets, (bet) => {
return sumBy(bets, (bet) => {
const contract = contractsDict[bet.contractId]
if (!contract || contract.isResolved) return 0
if (bet.sale || bet.isSold) return 0
@ -60,20 +60,20 @@ const computeInvestmentValue = async (
const computeTotalPool = async (
user: User,
contractsDict: _.Dictionary<Contract>
contractsDict: { [k: string]: Contract }
) => {
const creatorContracts = Object.values(contractsDict).filter(
(contract) => contract.creatorId === user.id
)
const pools = creatorContracts.map((contract) =>
_.sum(Object.values(contract.pool))
sum(Object.values(contract.pool))
)
return _.sum(pools)
return sum(pools)
}
const computeVolume = async (contract: Contract) => {
const bets = await getValues<Bet>(
firestore.collection(`contracts/${contract.id}/bets`)
)
return _.sumBy(bets, (bet) => Math.abs(bet.amount))
return sumBy(bets, (bet) => Math.abs(bet.amount))
}

View File

@ -9,10 +9,11 @@
"scripts": {},
"dependencies": {},
"devDependencies": {
"typescript": "4.6.4",
"@typescript-eslint/eslint-plugin": "5.25.0",
"@typescript-eslint/parser": "5.25.0",
"eslint": "8.15.0",
"prettier": "2.5.0"
"eslint-plugin-lodash": "^7.4.0",
"prettier": "2.5.0",
"typescript": "4.6.4"
}
}

View File

@ -1,9 +1,10 @@
module.exports = {
parser: '@typescript-eslint/parser',
plugins: ['lodash'],
extends: ['plugin:react-hooks/recommended', 'plugin:@next/next/recommended'],
rules: {
// Add or disable rules here.
'@next/next/no-img-element': 'off',
'@next/next/no-typos': 'off',
'lodash/import-scope': [2, 'member'],
},
}

View File

@ -1,6 +1,6 @@
import { Point, ResponsiveLine } from '@nivo/line'
import dayjs from 'dayjs'
import _ from 'lodash'
import { zip } from 'lodash'
import { useWindowSize } from 'web/hooks/use-window-size'
import { Col } from '../layout/col'
@ -16,7 +16,7 @@ export function DailyCountChart(props: {
dayjs(startDate).add(i, 'day').toDate()
)
const points = _.zip(dates, dailyCounts).map(([date, betCount]) => ({
const points = zip(dates, dailyCounts).map(([date, betCount]) => ({
x: date,
y: betCount,
}))
@ -68,7 +68,7 @@ export function DailyPercentChart(props: {
dayjs(startDate).add(i, 'day').toDate()
)
const points = _.zip(dates, dailyPercent).map(([date, betCount]) => ({
const points = zip(dates, dailyPercent).map(([date, betCount]) => ({
x: date,
y: betCount,
}))

View File

@ -1,5 +1,5 @@
import clsx from 'clsx'
import _ from 'lodash'
import { sum, mapValues } from 'lodash'
import { useState } from 'react'
import { DPM, FreeResponse, FullContract } from 'common/contract'
@ -30,8 +30,8 @@ export function AnswerResolvePanel(props: {
setIsSubmitting(true)
const totalProb = _.sum(Object.values(chosenAnswers))
const normalizedProbs = _.mapValues(
const totalProb = sum(Object.values(chosenAnswers))
const normalizedProbs = mapValues(
chosenAnswers,
(prob) => (100 * prob) / totalProb
)

View File

@ -1,7 +1,7 @@
import { DatumValue } from '@nivo/core'
import { ResponsiveLine } from '@nivo/line'
import dayjs from 'dayjs'
import _ from 'lodash'
import { groupBy, sortBy, sumBy } from 'lodash'
import { memo } from 'react'
import { Bet } from 'common/bet'
@ -48,7 +48,7 @@ export const AnswersGraph = memo(function AnswersGraph(props: {
// to the right.
latestTime.add(1, 'month').valueOf()
const times = _.sortBy([
const times = sortBy([
createdTime,
...bets.map((bet) => bet.createdTime),
endTime,
@ -167,7 +167,7 @@ const computeProbsByOutcome = (
) => {
const { totalBets } = contract
const betsByOutcome = _.groupBy(bets, (bet) => bet.outcome)
const betsByOutcome = groupBy(bets, (bet) => bet.outcome)
const outcomes = Object.keys(betsByOutcome).filter((outcome) => {
const maxProb = Math.max(
...betsByOutcome[outcome].map((bet) => bet.probAfter)
@ -175,15 +175,15 @@ const computeProbsByOutcome = (
return outcome !== '0' && maxProb > 0.02 && totalBets[outcome] > 0.000000001
})
const trackedOutcomes = _.sortBy(
const trackedOutcomes = sortBy(
outcomes,
(outcome) => -1 * getOutcomeProbability(contract, outcome)
).slice(0, NUM_LINES)
const probsByOutcome = _.fromPairs(
const probsByOutcome = Object.fromEntries(
trackedOutcomes.map((outcome) => [outcome, [] as number[]])
)
const sharesByOutcome = _.fromPairs(
const sharesByOutcome = Object.fromEntries(
Object.keys(betsByOutcome).map((outcome) => [outcome, 0])
)
@ -191,7 +191,7 @@ const computeProbsByOutcome = (
const { outcome, shares } = bet
sharesByOutcome[outcome] += shares
const sharesSquared = _.sumBy(
const sharesSquared = sumBy(
Object.values(sharesByOutcome).map((shares) => shares ** 2)
)

View File

@ -1,5 +1,5 @@
import _ from 'lodash'
import React, { useLayoutEffect, useState } from 'react'
import { sortBy, partition, sum, uniq } from 'lodash'
import { useLayoutEffect, useState } from 'react'
import { DPM, FreeResponse, FullContract } from 'common/contract'
import { Col } from '../layout/col'
@ -32,7 +32,7 @@ export function AnswersPanel(props: {
const { creatorId, resolution, resolutions, totalBets } = contract
const answers = useAnswers(contract.id) ?? contract.answers
const [winningAnswers, losingAnswers] = _.partition(
const [winningAnswers, losingAnswers] = partition(
answers.filter(
(answer) => answer.id !== '0' && totalBets[answer.id] > 0.000000001
),
@ -40,10 +40,10 @@ export function AnswersPanel(props: {
answer.id === resolution || (resolutions && resolutions[answer.id])
)
const sortedAnswers = [
..._.sortBy(winningAnswers, (answer) =>
...sortBy(winningAnswers, (answer) =>
resolutions ? -1 * resolutions[answer.id] : 0
),
..._.sortBy(
...sortBy(
resolution ? [] : losingAnswers,
(answer) => -1 * getDpmOutcomeProbability(contract.totalShares, answer.id)
),
@ -58,7 +58,7 @@ export function AnswersPanel(props: {
[answerId: string]: number
}>({})
const chosenTotal = _.sum(Object.values(chosenAnswers))
const chosenTotal = sum(Object.values(chosenAnswers))
const answerItems = getAnswerItems(
contract,
@ -158,10 +158,10 @@ function getAnswerItems(
answers: Answer[],
user: User | undefined | null
) {
let outcomes = _.uniq(
answers.map((answer) => answer.number.toString())
).filter((outcome) => getOutcomeProbability(contract, outcome) > 0.0001)
outcomes = _.sortBy(outcomes, (outcome) =>
let outcomes = uniq(answers.map((answer) => answer.number.toString())).filter(
(outcome) => getOutcomeProbability(contract, outcome) > 0.0001
)
outcomes = sortBy(outcomes, (outcome) =>
getOutcomeProbability(contract, outcome)
).reverse()

View File

@ -1,6 +1,6 @@
import clsx from 'clsx'
import _ from 'lodash'
import React, { useEffect, useState } from 'react'
import { partition, sumBy } from 'lodash'
import { useUser } from 'web/hooks/use-user'
import { Binary, CPMM, DPM, FullContract } from 'common/contract'
@ -441,13 +441,13 @@ export function SellPanel(props: {
const resultProb = getCpmmProbability(newPool, contract.p)
const openUserBets = userBets.filter((bet) => !bet.isSold && !bet.sale)
const [yesBets, noBets] = _.partition(
const [yesBets, noBets] = partition(
openUserBets,
(bet) => bet.outcome === 'YES'
)
const [yesShares, noShares] = [
_.sumBy(yesBets, (bet) => bet.shares),
_.sumBy(noBets, (bet) => bet.shares),
sumBy(yesBets, (bet) => bet.shares),
sumBy(noBets, (bet) => bet.shares),
]
const sellOutcome = yesShares ? 'YES' : noShares ? 'NO' : undefined

View File

@ -1,5 +1,13 @@
import Link from 'next/link'
import _ from 'lodash'
import {
uniq,
groupBy,
mapValues,
sortBy,
partition,
sumBy,
throttle,
} from 'lodash'
import dayjs from 'dayjs'
import { useEffect, useState } from 'react'
import clsx from 'clsx'
@ -54,7 +62,7 @@ export function BetsList(props: { user: User }) {
useEffect(() => {
if (bets) {
const contractIds = _.uniq(bets.map((bet) => bet.contractId))
const contractIds = uniq(bets.map((bet) => bet.contractId))
let disposed = false
Promise.all(contractIds.map((id) => getContractFromId(id))).then(
@ -84,10 +92,10 @@ export function BetsList(props: { user: User }) {
if (bets.length === 0) return <NoBets />
// Decending creation time.
bets.sort((bet1, bet2) => bet2.createdTime - bet1.createdTime)
const contractBets = _.groupBy(bets, 'contractId')
const contractsById = _.fromPairs(contracts.map((c) => [c.id, c]))
const contractBets = groupBy(bets, 'contractId')
const contractsById = Object.fromEntries(contracts.map((c) => [c.id, c]))
const contractsMetrics = _.mapValues(contractBets, (bets, contractId) => {
const contractsMetrics = mapValues(contractBets, (bets, contractId) => {
const contract = contractsById[contractId]
if (!contract) return getContractBetNullMetrics()
return getContractBetMetrics(contract, bets)
@ -110,7 +118,7 @@ export function BetsList(props: { user: User }) {
(filter === 'open' ? -1 : 1) *
(c.resolutionTime ?? c.closeTime ?? Infinity),
}
const displayedContracts = _.sortBy(contracts, SORTS[sort])
const displayedContracts = sortBy(contracts, SORTS[sort])
.reverse()
.filter(FILTERS[filter])
.filter((c) => {
@ -121,20 +129,20 @@ export function BetsList(props: { user: User }) {
return metrics.payout > 0
})
const [settled, unsettled] = _.partition(
const [settled, unsettled] = partition(
contracts,
(c) => c.isResolved || contractsMetrics[c.id].invested === 0
)
const currentInvested = _.sumBy(
const currentInvested = sumBy(
unsettled,
(c) => contractsMetrics[c.id].invested
)
const currentBetsValue = _.sumBy(
const currentBetsValue = sumBy(
unsettled,
(c) => contractsMetrics[c.id].payout
)
const currentNetInvestment = _.sumBy(
const currentNetInvestment = sumBy(
unsettled,
(c) => contractsMetrics[c.id].netPayout
)
@ -340,10 +348,10 @@ export function MyBetsSummary(props: {
const excludeSalesAndAntes = bets.filter(
(b) => !b.isAnte && !b.isSold && !b.sale
)
const yesWinnings = _.sumBy(excludeSalesAndAntes, (bet) =>
const yesWinnings = sumBy(excludeSalesAndAntes, (bet) =>
calculatePayout(contract, bet, 'YES')
)
const noWinnings = _.sumBy(excludeSalesAndAntes, (bet) =>
const noWinnings = sumBy(excludeSalesAndAntes, (bet) =>
calculatePayout(contract, bet, 'NO')
)
const { invested, profitPercent, payout, profit } = getContractBetMetrics(
@ -421,21 +429,19 @@ export function ContractBetsTable(props: {
const bets = props.bets.filter((b) => !b.isAnte)
const [sales, buys] = _.partition(bets, (bet) => bet.sale)
const [sales, buys] = partition(bets, (bet) => bet.sale)
const salesDict = _.fromPairs(
const salesDict = Object.fromEntries(
sales.map((sale) => [sale.sale?.betId ?? '', sale])
)
const [redemptions, normalBets] = _.partition(
const [redemptions, normalBets] = partition(
contract.mechanism === 'cpmm-1' ? bets : buys,
(b) => b.isRedemption
)
const amountRedeemed = Math.floor(
-0.5 * _.sumBy(redemptions, (b) => b.shares)
)
const amountRedeemed = Math.floor(-0.5 * sumBy(redemptions, (b) => b.shares))
const amountLoaned = _.sumBy(
const amountLoaned = sumBy(
bets.filter((bet) => !bet.isSold && !bet.sale),
(bet) => bet.loanAmount ?? 0
)
@ -570,10 +576,7 @@ function BetRow(props: { bet: Bet; contract: Contract; saleBet?: Bet }) {
)
}
const warmUpSellBet = _.throttle(
() => sellBet({}).catch(() => {}),
5000 /* ms */
)
const warmUpSellBet = throttle(() => sellBet({}).catch(() => {}), 5000 /* ms */)
function SellButton(props: { contract: Contract; bet: Bet }) {
useEffect(() => {

View File

@ -1,4 +1,3 @@
import _ from 'lodash'
import { useState } from 'react'
import { NumericContract } from 'common/contract'

View File

@ -1,5 +1,5 @@
import { StarIcon } from '@heroicons/react/solid'
import _ from 'lodash'
import { sumBy } from 'lodash'
import Link from 'next/link'
import Image from 'next/image'
import { Charity } from 'common/charity'
@ -11,7 +11,7 @@ export function CharityCard(props: { charity: Charity }) {
const { name, slug, photo, preview, id, tags } = props.charity
const txns = useCharityTxns(id)
const raised = _.sumBy(txns, (txn) => txn.amount)
const raised = sumBy(txns, (txn) => txn.amount)
return (
<Link href={`/charity/${slug}`} passHref>

View File

@ -1,7 +1,7 @@
import { DotsHorizontalIcon } from '@heroicons/react/outline'
import clsx from 'clsx'
import dayjs from 'dayjs'
import _ from 'lodash'
import { uniqBy, sum } from 'lodash'
import { useState } from 'react'
import { Bet } from 'common/bet'
@ -26,7 +26,7 @@ export function ContractInfoDialog(props: { contract: Contract; bets: Bet[] }) {
const formatTime = (dt: number) => dayjs(dt).format('MMM DD, YYYY hh:mm a z')
const { createdTime, closeTime, resolutionTime } = contract
const tradersCount = _.uniqBy(bets, 'userId').length
const tradersCount = uniqBy(bets, 'userId').length
return (
<>
@ -108,7 +108,7 @@ export function ContractInfoDialog(props: { contract: Contract; bets: Bet[] }) {
{contract.mechanism === 'dpm-2' && (
<tr>
<td>Pool</td>
<td>{formatMoney(_.sum(Object.values(contract.pool)))}</td>
<td>{formatMoney(sum(Object.values(contract.pool)))}</td>
</tr>
)}
</tbody>

View File

@ -1,8 +1,8 @@
import { DatumValue } from '@nivo/core'
import { ResponsiveLine } from '@nivo/line'
import { NUMERIC_GRAPH_COLOR } from 'common/numeric-constants'
import _ from 'lodash'
import { memo } from 'react'
import { range } from 'lodash'
import { getDpmOutcomeProbabilities } from '../../../common/calculate-dpm'
import { NumericContract } from '../../../common/contract'
import { useWindowSize } from '../../hooks/use-window-size'
@ -16,10 +16,10 @@ export const NumericGraph = memo(function NumericGraph(props: {
const bucketProbs = getDpmOutcomeProbabilities(totalShares)
const xs = _.range(bucketCount).map(
const xs = range(bucketCount).map(
(i) => min + ((max - min) * i) / bucketCount
)
const probs = _.range(bucketCount).map((i) => bucketProbs[`${i}`] * 100)
const probs = range(bucketCount).map((i) => bucketProbs[`${i}`] * 100)
const points = probs.map((prob, i) => ({ x: xs[i], y: prob }))
const maxProb = Math.max(...probs)
const data = [{ id: 'Probability', data: points, color: NUMERIC_GRAPH_COLOR }]

View File

@ -1,4 +1,4 @@
import _ from 'lodash'
import { sample } from 'lodash'
import { SparklesIcon, XIcon } from '@heroicons/react/solid'
import { Avatar } from './avatar'
import { useEffect, useRef, useState } from 'react'
@ -86,9 +86,7 @@ export default function FeedCreate(props: {
// Take care not to produce a different placeholder on the server and client
const [defaultPlaceholder, setDefaultPlaceholder] = useState('')
useEffect(() => {
setDefaultPlaceholder(
`e.g. ${_.sample(ENV_CONFIG.newQuestionPlaceholders)}`
)
setDefaultPlaceholder(`e.g. ${sample(ENV_CONFIG.newQuestionPlaceholders)}`)
}, [])
const placeholder = props.placeholder ?? defaultPlaceholder

View File

@ -1,4 +1,4 @@
import _ from 'lodash'
import { last, findLastIndex, uniq, sortBy } from 'lodash'
import { Answer } from 'common/answer'
import { Bet } from 'common/bet'
@ -200,17 +200,17 @@ function getAnswerGroups(
) {
const { sortByProb, abbreviated, reversed } = options
let outcomes = _.uniq(bets.map((bet) => bet.outcome)).filter(
let outcomes = uniq(bets.map((bet) => bet.outcome)).filter(
(outcome) => getOutcomeProbability(contract, outcome) > 0.0001
)
if (abbreviated) {
const lastComment = _.last(comments)
const lastComment = last(comments)
const lastCommentOutcome = bets.find(
(bet) => bet.id === lastComment?.betId
)?.outcome
const lastBetOutcome = _.last(bets)?.outcome
const lastBetOutcome = last(bets)?.outcome
if (lastCommentOutcome && lastBetOutcome) {
outcomes = _.uniq([
outcomes = uniq([
...outcomes.filter(
(outcome) =>
outcome !== lastCommentOutcome && outcome !== lastBetOutcome
@ -222,13 +222,13 @@ function getAnswerGroups(
outcomes = outcomes.slice(-2)
}
if (sortByProb) {
outcomes = _.sortBy(outcomes, (outcome) =>
outcomes = sortBy(outcomes, (outcome) =>
getOutcomeProbability(contract, outcome)
)
} else {
// Sort by recent bet.
outcomes = _.sortBy(outcomes, (outcome) =>
_.findLastIndex(bets, (bet) => bet.outcome === outcome)
outcomes = sortBy(outcomes, (outcome) =>
findLastIndex(bets, (bet) => bet.outcome === outcome)
)
}
@ -274,10 +274,10 @@ function getAnswerAndCommentInputGroups(
comments: Comment[],
user: User | undefined | null
) {
let outcomes = _.uniq(bets.map((bet) => bet.outcome)).filter(
let outcomes = uniq(bets.map((bet) => bet.outcome)).filter(
(outcome) => getOutcomeProbability(contract, outcome) > 0.0001
)
outcomes = _.sortBy(outcomes, (outcome) =>
outcomes = sortBy(outcomes, (outcome) =>
getOutcomeProbability(contract, outcome)
)
const betsByCurrentUser = bets.filter((bet) => bet.userId === user?.id)
@ -343,7 +343,7 @@ function groupBetsAndComments(
// iterate through the bets and comment activity items and add them to the items in order of comment creation time:
const unorderedBetsAndComments = [...commentsWithoutBets, ...groupedBets]
let sortedBetsAndComments = _.sortBy(unorderedBetsAndComments, (item) => {
let sortedBetsAndComments = sortBy(unorderedBetsAndComments, (item) => {
if (item.type === 'comment') {
return item.comment.createdTime
} else if (item.type === 'bet') {

View File

@ -10,7 +10,7 @@ import { formatMoney } from 'common/util/format'
import { OutcomeLabel } from 'web/components/outcome-label'
import { RelativeTimestamp } from 'web/components/relative-timestamp'
import React, { Fragment } from 'react'
import * as _ from 'lodash'
import { uniqBy, partition, sumBy, groupBy } from 'lodash'
import { JoinSpans } from 'web/components/join-spans'
export function FeedBet(props: {
@ -104,11 +104,11 @@ function BetGroupSpan(props: {
}) {
const { contract, bets, outcome } = props
const numberTraders = _.uniqBy(bets, (b) => b.userId).length
const numberTraders = uniqBy(bets, (b) => b.userId).length
const [buys, sells] = _.partition(bets, (bet) => bet.amount >= 0)
const buyTotal = _.sumBy(buys, (b) => b.amount)
const sellTotal = _.sumBy(sells, (b) => -b.amount)
const [buys, sells] = partition(bets, (bet) => bet.amount >= 0)
const buyTotal = sumBy(buys, (b) => b.amount)
const sellTotal = sumBy(sells, (b) => -b.amount)
return (
<span>
@ -139,7 +139,7 @@ export function FeedBetGroup(props: {
}) {
const { contract, bets, hideOutcome } = props
const betGroups = _.groupBy(bets, (bet) => bet.outcome)
const betGroups = groupBy(bets, (bet) => bet.outcome)
const outcomes = Object.keys(betGroups)
// Use the time of the last bet for the entire group

View File

@ -3,6 +3,7 @@ import { Comment } from 'common/comment'
import { User } from 'common/user'
import { Contract } from 'common/contract'
import React, { useEffect, useState } from 'react'
import { minBy, maxBy, groupBy, partition, sumBy } from 'lodash'
import { useUser } from 'web/hooks/use-user'
import { formatMoney } from 'common/util/format'
import { useRouter } from 'next/router'
@ -16,7 +17,6 @@ import { contractPath } from 'web/lib/firebase/contracts'
import { firebaseLogin } from 'web/lib/firebase/users'
import { createComment, MAX_COMMENT_LENGTH } from 'web/lib/firebase/comments'
import Textarea from 'react-expanding-textarea'
import * as _ from 'lodash'
import { Linkify } from 'web/components/linkify'
import { SiteLink } from 'web/components/site-link'
import { BetStatusText } from 'web/components/feed/feed-bets'
@ -36,7 +36,7 @@ export function FeedCommentThread(props: {
props
const [showReply, setShowReply] = useState(false)
const [replyToUsername, setReplyToUsername] = useState('')
const betsByUserId = _.groupBy(bets, (bet) => bet.userId)
const betsByUserId = groupBy(bets, (bet) => bet.userId)
const user = useUser()
const commentsList = comments.filter(
(comment) =>
@ -67,7 +67,7 @@ export function FeedCommentThread(props: {
onReplyClick={scrollAndOpenReplyInput}
probAtCreatedTime={
contract.outcomeType === 'BINARY'
? _.minBy(bets, (bet) => {
? minBy(bets, (bet) => {
return bet.createdTime < comment.createdTime
? comment.createdTime - bet.createdTime
: comment.createdTime
@ -494,8 +494,7 @@ function getBettorsLargestPositionBeforeTime(
}
}
const majorityAnswer =
_.maxBy(Object.keys(answerCounts), (outcome) => answerCounts[outcome]) ??
''
maxBy(Object.keys(answerCounts), (outcome) => answerCounts[outcome]) ?? ''
return {
userPosition: answerCounts[majorityAnswer] || 0,
outcome: majorityAnswer,
@ -505,12 +504,12 @@ function getBettorsLargestPositionBeforeTime(
return emptyReturn
}
const [yesBets, noBets] = _.partition(
const [yesBets, noBets] = partition(
previousBets ?? [],
(bet) => bet.outcome === 'YES'
)
yesShares = _.sumBy(yesBets, (bet) => bet.shares)
noShares = _.sumBy(noBets, (bet) => bet.shares)
yesShares = sumBy(yesBets, (bet) => bet.shares)
noShares = sumBy(noBets, (bet) => bet.shares)
yesFloorShares = Math.floor(yesShares)
noFloorShares = Math.floor(noShares)

View File

@ -1,6 +1,5 @@
// From https://tailwindui.com/components/application-ui/lists/feeds
import React, { useState } from 'react'
import * as _ from 'lodash'
import {
BanIcon,
CheckIcon,

View File

@ -1,4 +1,4 @@
import _ from 'lodash'
import { groupBy, mapValues, maxBy, sortBy } from 'lodash'
import { Contract } from 'web/lib/firebase/contracts'
import { Comment } from 'web/lib/firebase/comments'
import { Bet } from 'common/bet'
@ -44,10 +44,10 @@ export function findActiveContracts(
}
// Add contracts by last bet time.
const contractBets = _.groupBy(recentBets, (bet) => bet.contractId)
const contractMostRecentBet = _.mapValues(
const contractBets = groupBy(recentBets, (bet) => bet.contractId)
const contractMostRecentBet = mapValues(
contractBets,
(bets) => _.maxBy(bets, (bet) => bet.createdTime) as Bet
(bets) => maxBy(bets, (bet) => bet.createdTime) as Bet
)
for (const bet of Object.values(contractMostRecentBet)) {
const contract = contractsById.get(bet.contractId)
@ -60,21 +60,21 @@ export function findActiveContracts(
!contract.isResolved &&
(contract.closeTime ?? Infinity) > Date.now()
)
activeContracts = _.sortBy(
activeContracts = sortBy(
activeContracts,
(c) => -(idToActivityTime.get(c.id) ?? 0)
)
const contractComments = _.groupBy(
const contractComments = groupBy(
recentComments,
(comment) => comment.contractId
)
const contractMostRecentComment = _.mapValues(
const contractMostRecentComment = mapValues(
contractComments,
(comments) => _.maxBy(comments, (c) => c.createdTime) as Comment
(comments) => maxBy(comments, (c) => c.createdTime) as Comment
)
const prioritizedContracts = _.sortBy(activeContracts, (c) => {
const prioritizedContracts = sortBy(activeContracts, (c) => {
const seenTime = seenContracts[c.id]
if (!seenTime) {
return 0

View File

@ -1,5 +1,5 @@
import { useState } from 'react'
import _ from 'lodash'
import { isEqual } from 'lodash'
import clsx from 'clsx'
import { PencilIcon } from '@heroicons/react/outline'
@ -29,7 +29,7 @@ export function EditFoldButton(props: { fold: Fold; className?: string }) {
const saveDisabled =
name === fold.name &&
_.isEqual(tags, fold.tags) &&
isEqual(tags, fold.tags) &&
about === (fold.about ?? '')
const onSubmit = async () => {

View File

@ -11,7 +11,7 @@ import {
SparklesIcon,
} from '@heroicons/react/outline'
import clsx from 'clsx'
import _ from 'lodash'
import { sortBy } from 'lodash'
import Link from 'next/link'
import { useRouter } from 'next/router'
import { useFollowedFolds } from 'web/hooks/use-fold'
@ -124,7 +124,7 @@ export default function Sidebar(props: { className?: string }) {
const user = useUser()
let folds = useFollowedFolds(user) || []
folds = _.sortBy(folds, 'followCount').reverse()
folds = sortBy(folds, 'followCount').reverse()
const deservesDailyFreeMarket = !useHasCreatedContractToday(user)
const navigationOptions =

View File

@ -1,5 +1,4 @@
import clsx from 'clsx'
import _ from 'lodash'
import { Col } from './layout/col'
import { Spacer } from './layout/spacer'

View File

@ -1,7 +1,7 @@
import { Binary, CPMM, DPM, FullContract } from 'common/contract'
import { Bet } from 'common/bet'
import { useEffect, useState } from 'react'
import _ from 'lodash'
import { partition, sumBy } from 'lodash'
export const useSaveShares = (
contract: FullContract<CPMM | DPM, Binary>,
@ -17,13 +17,13 @@ export const useSaveShares = (
| undefined
>()
const [yesBets, noBets] = _.partition(
const [yesBets, noBets] = partition(
userBets ?? [],
(bet) => bet.outcome === 'YES'
)
const [yesShares, noShares] = [
_.sumBy(yesBets, (bet) => bet.shares),
_.sumBy(noBets, (bet) => bet.shares),
sumBy(yesBets, (bet) => bet.shares),
sumBy(noBets, (bet) => bet.shares),
]
const yesFloorShares = Math.round(yesShares) === 0 ? 0 : Math.floor(yesShares)

View File

@ -19,10 +19,10 @@ import { Comment, getUsersComments } from 'web/lib/firebase/comments'
import { Contract } from 'common/contract'
import { getContractFromId, listContracts } from 'web/lib/firebase/contracts'
import { LoadingIndicator } from './loading-indicator'
import _ from 'lodash'
import { BetsList } from './bets-list'
import { Bet } from 'common/bet'
import { getUserBets } from 'web/lib/firebase/bets'
import { uniq } from 'lodash'
export function UserLink(props: {
name: string
@ -70,7 +70,7 @@ export function UserPage(props: {
}, [user])
useEffect(() => {
const uniqueContractIds = _.uniq(
const uniqueContractIds = uniq(
usersComments.map((comment) => comment.contractId)
)
Promise.all(

View File

@ -1,4 +1,3 @@
import _, { Dictionary } from 'lodash'
import { useState, useEffect } from 'react'
import type { feed } from 'common/feed'
import { useTimeSinceFirstRender } from './use-time-since-first-render'
@ -15,7 +14,7 @@ export const useAlgoFeed = (
category: string
) => {
const [allFeed, setAllFeed] = useState<feed>()
const [categoryFeeds, setCategoryFeeds] = useState<Dictionary<feed>>()
const [categoryFeeds, setCategoryFeeds] = useState<{ [x: string]: feed }>()
const getTime = useTimeSinceFirstRender()

View File

@ -1,4 +1,4 @@
import _ from 'lodash'
import { isEqual } from 'lodash'
import { useEffect, useRef, useState } from 'react'
import {
Contract,
@ -80,14 +80,14 @@ export const useUpdatedContracts = (contracts: Contract[] | undefined) => {
useEffect(() => {
if (contracts === undefined) return
contractDict.current = _.fromPairs(contracts.map((c) => [c.id, c]))
contractDict.current = Object.fromEntries(contracts.map((c) => [c.id, c]))
const disposes = contracts.map((contract) => {
const { id } = contract
return listenForContract(id, (contract) => {
const curr = contractDict.current[id]
if (!_.isEqual(curr, contract)) {
if (!isEqual(curr, contract)) {
contractDict.current[id] = contract as Contract
triggerUpdate((n) => n + 1)
}

View File

@ -1,4 +1,4 @@
import _ from 'lodash'
import { isEqual, sortBy } from 'lodash'
import { useEffect, useState } from 'react'
import { Fold } from 'common/fold'
import { User } from 'common/user'
@ -95,9 +95,9 @@ export const useFollowedFolds = (user: User | null | undefined) => {
setFollowedFolds(JSON.parse(followedFoldJson))
// Exit early if ids and followedFoldIds have all the same elements.
if (
_.isEqual(
_.sortBy(ids),
_.sortBy(JSON.parse(followedFoldJson).map((f: Fold) => f.id))
isEqual(
sortBy(ids),
sortBy(JSON.parse(followedFoldJson).map((f: Fold) => f.id))
)
) {
return

View File

@ -1,4 +1,4 @@
import _ from 'lodash'
import { debounce } from 'lodash'
import { RefObject, useMemo, useLayoutEffect, useRef, useState } from 'react'
type elem_size =
@ -21,7 +21,7 @@ export function useListenElemSize<T extends HTMLElement>(
}
return debounceMs
? _.debounce(updateSize, debounceMs, { leading: false, trailing: true })
? debounce(updateSize, debounceMs, { leading: false, trailing: true })
: updateSize
}, [callback, elemRef, debounceMs])

View File

@ -1,4 +1,4 @@
import _ from 'lodash'
import { isEmpty } from 'lodash'
import { useRouter } from 'next/router'
import { useState, useEffect } from 'react'
import { IS_PRIVATE_MANIFOLD } from 'common/envs/constants'
@ -16,7 +16,7 @@ export function usePropz(
getStaticPropz: (props: PropzProps) => Promise<any>
) {
// If props were successfully server-side generated, just use those
if (!_.isEmpty(initialProps)) {
if (!isEmpty(initialProps)) {
return initialProps
}

View File

@ -1,4 +1,4 @@
import _ from 'lodash'
import { mapValues } from 'lodash'
import { useEffect, useState } from 'react'
import { Contract } from 'common/contract'
import { trackView } from 'web/lib/firebase/tracking'
@ -38,7 +38,7 @@ export const useSaveSeenContract = (
const key = 'feed-seen-contracts'
const getSeenContracts = () => {
return _.mapValues(
return mapValues(
JSON.parse(localStorage.getItem(key) ?? '{}'),
(time) => +time
)

View File

@ -1,4 +1,4 @@
import _ from 'lodash'
import { defaults, debounce } from 'lodash'
import { useRouter } from 'next/router'
import { useEffect, useMemo, useState } from 'react'
import { useSearchBox } from 'react-instantsearch-hooks-web'
@ -18,7 +18,7 @@ export function useInitialQueryAndSort(options?: {
defaultSort: Sort
shouldLoadFromStorage?: boolean
}) {
const { defaultSort, shouldLoadFromStorage } = _.defaults(options, {
const { defaultSort, shouldLoadFromStorage } = defaults(options, {
defaultSort: '24-hour-vol',
shouldLoadFromStorage: true,
})
@ -79,7 +79,7 @@ export function useUpdateQueryAndSort(props: {
// Debounce router query update.
const pushQuery = useMemo(
() =>
_.debounce((query: string | undefined) => {
debounce((query: string | undefined) => {
if (query) {
router.query.q = query
} else {

View File

@ -1,4 +1,4 @@
import _ from 'lodash'
import { isEqual } from 'lodash'
import { useMemo, useRef, useState } from 'react'
export const useStateCheckEquality = <T>(initialState: T) => {
@ -10,7 +10,7 @@ export const useStateCheckEquality = <T>(initialState: T) => {
const checkSetState = useMemo(
() => (newState: T) => {
const state = stateRef.current
if (!_.isEqual(state, newState)) {
if (!isEqual(state, newState)) {
setState(newState)
}
},

View File

@ -1,4 +1,4 @@
import _ from 'lodash'
import { uniq } from 'lodash'
import { useEffect, useState } from 'react'
import {
Bet,
@ -51,7 +51,7 @@ export const useUserBetContracts = (
return listenForUserBets(
userId,
(bets) => {
const contractIds = _.uniq(bets.map((bet) => bet.contractId))
const contractIds = uniq(bets.map((bet) => bet.contractId))
setContractIds(contractIds)
localStorage.setItem(key, JSON.stringify(contractIds))
},

View File

@ -1,4 +1,3 @@
import _ from 'lodash'
import { useEffect, useState } from 'react'
import { PrivateUser } from 'common/user'
import {

View File

@ -5,7 +5,7 @@ import {
where,
orderBy,
} from 'firebase/firestore'
import _ from 'lodash'
import { range } from 'lodash'
import { db } from './init'
import { Bet } from 'common/bet'
@ -138,7 +138,7 @@ export async function getDailyBets(startTime: number, numberOfDays: number) {
const query = getBetsQuery(startTime, startTime + DAY_IN_MS * numberOfDays)
const bets = await getValues<Bet>(query)
const betsByDay = _.range(0, numberOfDays).map(() => [] as Bet[])
const betsByDay = range(0, numberOfDays).map(() => [] as Bet[])
for (const bet of bets) {
const dayIndex = Math.floor((bet.createdTime - startTime) / DAY_IN_MS)
betsByDay[dayIndex].push(bet)

View File

@ -7,7 +7,7 @@ import {
setDoc,
where,
} from 'firebase/firestore'
import _ from 'lodash'
import { range } from 'lodash'
import { getValues, listenForValues } from './utils'
import { db } from './init'
@ -117,7 +117,7 @@ export async function getDailyComments(
)
const comments = await getValues<Comment>(query)
const commentsByDay = _.range(0, numberOfDays).map(() => [] as Comment[])
const commentsByDay = range(0, numberOfDays).map(() => [] as Comment[])
for (const comment of comments) {
const dayIndex = Math.floor((comment.createdTime - startTime) / DAY_IN_MS)
commentsByDay[dayIndex].push(comment)

View File

@ -13,7 +13,7 @@ import {
updateDoc,
limit,
} from 'firebase/firestore'
import _ from 'lodash'
import { range, sortBy } from 'lodash'
import { app } from './init'
import { getValues, listenForValue, listenForValues } from './utils'
@ -225,7 +225,7 @@ export function listenForHotContracts(
setHotContracts: (contracts: Contract[]) => void
) {
return listenForValues<Contract>(hotContractsQuery, (contracts) => {
const hotContracts = _.sortBy(
const hotContracts = sortBy(
chooseRandomSubset(contracts, 4),
(contract) => contract.volume24Hours
)
@ -235,7 +235,7 @@ export function listenForHotContracts(
export async function getHotContracts() {
const contracts = await getValues<Contract>(hotContractsQuery)
return _.sortBy(
return sortBy(
chooseRandomSubset(contracts, 10),
(contract) => -1 * contract.volume24Hours
)
@ -245,7 +245,7 @@ export async function getContractsBySlugs(slugs: string[]) {
const q = query(contractCollection, where('slug', 'in', slugs))
const snapshot = await getDocs(q)
const contracts = snapshot.docs.map((doc) => doc.data() as Contract)
return _.sortBy(contracts, (contract) => -1 * contract.volume24Hours)
return sortBy(contracts, (contract) => -1 * contract.volume24Hours)
}
const topWeeklyQuery = query(
@ -269,7 +269,7 @@ const closingSoonQuery = query(
export async function getClosingSoonContracts() {
const contracts = await getValues<Contract>(closingSoonQuery)
return _.sortBy(
return sortBy(
chooseRandomSubset(contracts, 2),
(contract) => contract.closeTime
)
@ -295,7 +295,7 @@ export async function getDailyContracts(
)
const contracts = await getValues<Contract>(query)
const contractsByDay = _.range(0, numberOfDays).map(() => [] as Contract[])
const contractsByDay = range(0, numberOfDays).map(() => [] as Contract[])
for (const contract of contracts) {
const dayIndex = Math.floor((contract.createdTime - startTime) / DAY_IN_MS)
contractsByDay[dayIndex].push(contract)

View File

@ -10,7 +10,7 @@ import {
updateDoc,
where,
} from 'firebase/firestore'
import _ from 'lodash'
import { sortBy } from 'lodash'
import { Fold } from 'common/fold'
import { Contract, contractCollection } from './contracts'
import { db } from './init'
@ -171,7 +171,7 @@ export async function getFoldsByTags(tags: string[]) {
)
)
return _.sortBy(folds, (fold) => -1 * fold.followCount)
return sortBy(folds, (fold) => -1 * fold.followCount)
}
export function listenForFoldsWithTags(
@ -187,7 +187,7 @@ export function listenForFoldsWithTags(
)
return listenForValues<Fold>(q, (folds) => {
const sorted = _.sortBy(folds, (fold) => -1 * fold.followCount)
const sorted = sortBy(folds, (fold) => -1 * fold.followCount)
setFolds(sorted)
})
}

View File

@ -18,7 +18,7 @@ import {
GoogleAuthProvider,
signInWithPopup,
} from 'firebase/auth'
import _ from 'lodash'
import { range, throttle, zip } from 'lodash'
import { app } from './init'
import { PrivateUser, User } from 'common/user'
@ -83,7 +83,7 @@ const CACHED_USER_KEY = 'CACHED_USER_KEY'
// used to avoid weird race condition
let createUserPromise: Promise<User | null> | undefined = undefined
const warmUpCreateUser = _.throttle(createUser, 5000 /* ms */)
const warmUpCreateUser = throttle(createUser, 5000 /* ms */)
export function listenForLogin(onUser: (user: User | null) => void) {
const cachedUser = localStorage.getItem(CACHED_USER_KEY)
@ -210,7 +210,7 @@ export async function getDailyNewUsers(
const query = getUsersQuery(startTime, startTime + DAY_MS * numberOfDays)
const users = await getValues<User>(query)
const usersByDay = _.range(0, numberOfDays).map(() => [] as User[])
const usersByDay = range(0, numberOfDays).map(() => [] as User[])
for (const user of users) {
const dayIndex = Math.floor((user.createdTime - startTime) / DAY_MS)
usersByDay[dayIndex].push(user)
@ -235,5 +235,5 @@ export async function getCategoryFeeds(userId: string) {
)
)
const feeds = feedData.map((data) => data?.feed ?? [])
return _.fromPairs(_.zip(CATEGORY_LIST, feeds) as [string, feed][])
return Object.fromEntries(zip(CATEGORY_LIST, feeds) as [string, feed][])
}

View File

@ -6,6 +6,11 @@ module.exports = {
experimental: {
externalDir: true,
optimizeCss: true,
modularizeImports: {
lodash: {
transform: 'lodash/{{member}}',
},
},
},
images: {
domains: ['lh3.googleusercontent.com', 'i.imgur.com'],

View File

@ -1,6 +1,6 @@
import React, { useEffect, useMemo, useState } from 'react'
import { ArrowLeftIcon } from '@heroicons/react/outline'
import _ from 'lodash'
import { keyBy, sortBy, groupBy, sumBy, mapValues } from 'lodash'
import { useContractWithPreload } from 'web/hooks/use-contract'
import { ContractOverview } from 'web/components/contract/contract-overview'
@ -250,13 +250,13 @@ function ContractLeaderboard(props: { contract: Contract; bets: Bet[] }) {
const { userProfits, top5Ids } = useMemo(() => {
// Create a map of userIds to total profits (including sales)
const betsByUser = _.groupBy(bets, 'userId')
const userProfits = _.mapValues(betsByUser, (bets) =>
_.sumBy(bets, (bet) => resolvedPayout(contract, bet) - bet.amount)
const betsByUser = groupBy(bets, 'userId')
const userProfits = mapValues(betsByUser, (bets) =>
sumBy(bets, (bet) => resolvedPayout(contract, bet) - bet.amount)
)
// Find the 5 users with the most profits
const top5Ids = _.entries(userProfits)
.sort(([i1, p1], [i2, p2]) => p2 - p1)
const top5Ids = Object.entries(userProfits)
.sort(([_i1, p1], [_i2, p2]) => p2 - p1)
.filter(([, p]) => p > 0)
.slice(0, 5)
.map(([id]) => id)
@ -267,7 +267,7 @@ function ContractLeaderboard(props: { contract: Contract; bets: Bet[] }) {
console.log('foo')
if (top5Ids.length > 0) {
listUsers(top5Ids).then((users) => {
const sortedUsers = _.sortBy(users, (user) => -userProfits[user.id])
const sortedUsers = sortBy(users, (user) => -userProfits[user.id])
setUsers(sortedUsers)
})
}
@ -294,8 +294,8 @@ function ContractTopTrades(props: {
comments: Comment[]
}) {
const { contract, bets, comments } = props
const commentsById = _.keyBy(comments, 'id')
const betsById = _.keyBy(bets, 'id')
const commentsById = keyBy(comments, 'id')
const betsById = keyBy(bets, 'id')
// If 'id2' is the sale of 'id1', both are logged with (id2 - id1) of profit
// Otherwise, we record the profit at resolution time
@ -312,11 +312,11 @@ function ContractTopTrades(props: {
}
// Now find the betId with the highest profit
const topBetId = _.sortBy(bets, (b) => -profitById[b.id])[0]?.id
const topBetId = sortBy(bets, (b) => -profitById[b.id])[0]?.id
const topBettor = useUserById(betsById[topBetId]?.userId)
// And also the commentId of the comment with the highest profit
const topCommentId = _.sortBy(
const topCommentId = sortBy(
comments,
(c) => c.betId && -profitById[c.betId]
)[0]?.id

View File

@ -6,7 +6,7 @@ import dayjs from 'dayjs'
import { usePrivateUsers, useUsers } from 'web/hooks/use-users'
import Custom404 from './404'
import { useContracts } from 'web/hooks/use-contracts'
import _ from 'lodash'
import { mapKeys } from 'lodash'
import { useAdmin } from 'web/hooks/use-admin'
import { contractPath } from 'web/lib/firebase/contracts'
@ -23,7 +23,7 @@ function UsersTable() {
let privateUsers = usePrivateUsers()
// Map private users by user id
const privateUsersById = _.mapKeys(privateUsers, 'id')
const privateUsersById = mapKeys(privateUsers, 'id')
console.log('private users by id', privateUsersById)
// For each user, set their email from the PrivateUser
@ -64,7 +64,7 @@ function UsersTable() {
id: 'username',
name: 'Username',
formatter: (cell) =>
html(`<a
html(`<a
class="hover:underline hover:decoration-indigo-400 hover:decoration-2"
href="/${cell}">@${cell}</a>`),
},
@ -134,7 +134,7 @@ function ContractsTable() {
id: 'creatorUsername',
name: 'Username',
formatter: (cell) =>
html(`<a
html(`<a
class="hover:underline hover:decoration-indigo-400 hover:decoration-2"
target="_blank"
href="/${cell}">@${cell}</a>`),

View File

@ -1,5 +1,5 @@
import dayjs from 'dayjs'
import _ from 'lodash'
import { zip, uniq, sumBy } from 'lodash'
import { IS_PRIVATE_MANIFOLD } from 'common/envs/constants'
import {
DailyCountChart,
@ -40,12 +40,12 @@ export async function getStaticPropz() {
)
const dailyCommentCounts = dailyComments.map((comments) => comments.length)
const dailyUserIds = _.zip(dailyContracts, dailyBets, dailyComments).map(
const dailyUserIds = zip(dailyContracts, dailyBets, dailyComments).map(
([contracts, bets, comments]) => {
const creatorIds = (contracts ?? []).map((c) => c.creatorId)
const betUserIds = (bets ?? []).map((bet) => bet.userId)
const commentUserIds = (comments ?? []).map((comment) => comment.userId)
return _.uniq([...creatorIds, ...betUserIds, ...commentUserIds])
return uniq([...creatorIds, ...betUserIds, ...commentUserIds])
}
)
@ -87,7 +87,7 @@ export async function getStaticPropz() {
for (let j = lastWeek.start; j <= lastWeek.end; j++) {
dailyUserIds[j].forEach((userId) => activeLastWeek.add(userId))
}
const retainedCount = _.sumBy(Array.from(activeTwoWeeksAgo), (userId) =>
const retainedCount = sumBy(Array.from(activeTwoWeeksAgo), (userId) =>
activeLastWeek.has(userId) ? 1 : 0
)
const retainedFrac = retainedCount / activeTwoWeeksAgo.size
@ -112,7 +112,7 @@ export async function getStaticPropz() {
for (let j = lastMonth.start; j <= lastMonth.end; j++) {
dailyUserIds[j].forEach((userId) => activeLastMonth.add(userId))
}
const retainedCount = _.sumBy(Array.from(activeTwoMonthsAgo), (userId) =>
const retainedCount = sumBy(Array.from(activeTwoMonthsAgo), (userId) =>
activeLastMonth.has(userId) ? 1 : 0
)
const retainedFrac = retainedCount / activeTwoMonthsAgo.size

View File

@ -1,4 +1,4 @@
import _ from 'lodash'
import { sortBy, sumBy, uniqBy } from 'lodash'
import clsx from 'clsx'
import { useEffect, useRef, useState } from 'react'
import { Col } from 'web/components/layout/col'
@ -41,13 +41,13 @@ function CharityPage(props: { charity: Charity }) {
const user = useUser()
const txns = useCharityTxns(charity.id)
const newToOld = _.sortBy(txns, (txn) => -txn.createdTime)
const totalRaised = _.sumBy(txns, (txn) => txn.amount)
const fromYou = _.sumBy(
const newToOld = sortBy(txns, (txn) => -txn.createdTime)
const totalRaised = sumBy(txns, (txn) => txn.amount)
const fromYou = sumBy(
txns.filter((txn) => txn.fromId === user?.id),
(txn) => txn.amount
)
const numSupporters = _.uniqBy(txns, (txn) => txn.fromId).length
const numSupporters = uniqBy(txns, (txn) => txn.fromId).length
const { width, height } = useWindowSize()
const [showConfetti, setShowConfetti] = useState(false)

View File

@ -1,4 +1,4 @@
import _ from 'lodash'
import { mapValues, groupBy, sumBy, sum, sortBy, debounce } from 'lodash'
import { useState, useMemo } from 'react'
import { charities, Charity as CharityType } from 'common/charity'
import { CharityCard } from 'web/components/charity/charity-card'
@ -12,11 +12,11 @@ import { formatMoney } from 'common/util/format'
export async function getStaticProps() {
const txns = await getAllCharityTxns()
const totals = _.mapValues(_.groupBy(txns, 'toId'), (txns) =>
_.sumBy(txns, (txn) => txn.amount)
const totals = mapValues(groupBy(txns, 'toId'), (txns) =>
sumBy(txns, (txn) => txn.amount)
)
const totalRaised = _.sum(Object.values(totals))
const sortedCharities = _.sortBy(charities, [
const totalRaised = sum(Object.values(totals))
const sortedCharities = sortBy(charities, [
(charity) => (charity.tags?.includes('Featured') ? 0 : 1),
(charity) => -totals[charity.id],
])
@ -37,7 +37,7 @@ export default function Charity(props: {
const { totalRaised, charities } = props
const [query, setQuery] = useState('')
const debouncedQuery = _.debounce(setQuery, 50)
const debouncedQuery = debounce(setQuery, 50)
const filterCharities = useMemo(
() =>

View File

@ -1,4 +1,4 @@
import _ from 'lodash'
import { flatten, take, partition, sortBy } from 'lodash'
import { Fold } from 'common/fold'
import { Comment } from 'common/comment'
@ -47,8 +47,8 @@ export async function getStaticPropz(props: { params: { slugs: string[] } }) {
contracts.map((contract) => listAllBets(contract.id))
)
let activeContracts = findActiveContracts(contracts, [], _.flatten(bets), {})
const [resolved, unresolved] = _.partition(
let activeContracts = findActiveContracts(contracts, [], flatten(bets), {})
const [resolved, unresolved] = partition(
activeContracts,
({ isResolved }) => isResolved
)
@ -80,8 +80,8 @@ export async function getStaticPropz(props: { params: { slugs: string[] } }) {
}
async function toTopUsers(userScores: { [userId: string]: number }) {
const topUserPairs = _.take(
_.sortBy(Object.entries(userScores), ([_, score]) => -1 * score),
const topUserPairs = take(
sortBy(Object.entries(userScores), ([_, score]) => -1 * score),
10
).filter(([_, score]) => score >= 0.5)
@ -134,7 +134,7 @@ export default function FoldPage(props: {
const isCurator = user && fold && user.id === fold.curatorId
const taggedContracts = useTaggedContracts(fold?.tags) ?? props.contracts
const contractsMap = _.fromPairs(
const contractsMap = Object.fromEntries(
taggedContracts.map((contract) => [contract.id, contract])
)

View File

@ -1,4 +1,4 @@
import _ from 'lodash'
import { sortBy, debounce } from 'lodash'
import Link from 'next/link'
import { useEffect, useState } from 'react'
import { Fold } from 'common/fold'
@ -21,7 +21,7 @@ export async function getStaticProps() {
const curators = await Promise.all(
folds.map((fold) => getUser(fold.curatorId))
)
const curatorsDict = _.fromPairs(
const curatorsDict = Object.fromEntries(
curators.map((curator) => [curator.id, curator])
)
@ -37,7 +37,7 @@ export async function getStaticProps() {
export default function Folds(props: {
folds: Fold[]
curatorsDict: _.Dictionary<User>
curatorsDict: { [k: string]: User }
}) {
const [curatorsDict, setCuratorsDict] = useState(props.curatorsDict)
@ -51,7 +51,7 @@ export default function Folds(props: {
if (newFolds.length > 0) {
Promise.all(newFolds.map(({ curatorId }) => getUser(curatorId))).then(
(newUsers) => {
const newUsersDict = _.fromPairs(
const newUsersDict = Object.fromEntries(
newUsers.map((user) => [user.id, user])
)
setCuratorsDict({ ...curatorsDict, ...newUsersDict })
@ -68,7 +68,7 @@ export default function Folds(props: {
}
// List followed folds first, then folds with the highest follower count
const matches = _.sortBy(folds, [
const matches = sortBy(folds, [
(fold) => !followedFoldIds.includes(fold.id),
(fold) => -1 * fold.followCount,
]).filter(
@ -79,7 +79,7 @@ export default function Folds(props: {
check(f.lowercaseTags.map((tag) => `#${tag}`).join(' '))
)
// Not strictly necessary, but makes the "hold delete" experience less laggy
const debouncedQuery = _.debounce(setQuery, 50)
const debouncedQuery = debounce(setQuery, 50)
return (
<Page>

View File

@ -1,4 +1,4 @@
import _ from 'lodash'
import { sortBy } from 'lodash'
import { GetServerSideProps } from 'next'
import { getServerSideSitemap, ISitemapField } from 'next-sitemap'
@ -10,7 +10,7 @@ export const getServerSideProps: GetServerSideProps = async (ctx) => {
const response = await fetch(`https://${DOMAIN}/api/v0/markets`)
const liteMarkets = (await response.json()) as LiteMarket[]
const sortedMarkets = _.sortBy(liteMarkets, (m) => -m.volume24Hours)
const sortedMarkets = sortBy(liteMarkets, (m) => -m.volume24Hours)
const fields = sortedMarkets.map((market) => ({
// See https://www.sitemaps.org/protocol.html

View File

@ -2394,6 +2394,13 @@ eslint-plugin-jsx-a11y@^6.5.1:
language-tags "^1.0.5"
minimatch "^3.0.4"
eslint-plugin-lodash@^7.4.0:
version "7.4.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-lodash/-/eslint-plugin-lodash-7.4.0.tgz#14a761547f126c92ff56789662a20a44f8bb6290"
integrity sha512-Tl83UwVXqe1OVeBRKUeWcfg6/pCW1GTRObbdnbEJgYwjxp5Q92MEWQaH9+dmzbRt6kvYU1Mp893E79nJiCSM8A==
dependencies:
lodash "^4.17.21"
eslint-plugin-react-hooks@^4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.5.0.tgz#5f762dfedf8b2cf431c689f533c9d3fa5dcf25ad"