Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New @vueuse/math package #1812

Closed
3 of 6 tasks
antfu opened this issue Jul 10, 2022 · 12 comments
Closed
3 of 6 tasks

New @vueuse/math package #1812

antfu opened this issue Jul 10, 2022 · 12 comments
Labels
help wanted Extra attention is needed pr welcome
Milestone

Comments

@antfu
Copy link
Member

antfu commented Jul 10, 2022

We plan to ship a new @vueuse/math package in v9.0, where you can see it in the next branch already.

We planned to adding those new functions:

Open for contributions

@antfu antfu added help wanted Extra attention is needed pr welcome labels Jul 10, 2022
@antfu antfu added this to the 9.0 milestone Jul 10, 2022
@antfu antfu mentioned this issue Jul 10, 2022
8 tasks
@LittleSound
Copy link
Contributor

LittleSound commented Jul 10, 2022

For some of the single parameter Composables maybe we can support setter ?

Example useRound:

const round = useRound(0.5)

watchSyncEffect(() => console.log(round.value)) // log: 1

round.value = 0.1 // log: 0

round.value = 2.5 // log: 3

@antfu
Copy link
Member Author

antfu commented Jul 10, 2022

@LittleSound Yes we could. But how should we handle this case?:

const value = ref(0.5)
const round = useRound(value)

watchSyncEffect(() => console.log(value)) // log: 1

round.value = 0.1 // log: 0 // value?

round.value = 2.5 // log: 3

@LittleSound
Copy link
Contributor

LittleSound commented Jul 10, 2022

@antfu 😂 I made a typo, it should be watchSyncEffect(() => console.log(round.value))

I fixed it

@antfu
Copy link
Member Author

antfu commented Jul 10, 2022

No problem with the typo. My question is, how should we handle the write back if the value is provided as a ref?

@LittleSound
Copy link
Contributor

LittleSound commented Jul 10, 2022

@LittleSound是的,我们可以。但是我们应该如何处理这种情况呢?:

const value = ref(0.5)
const round = useRound(value)

watchSyncEffect(() => console.log(value)) // log: 1

round.value = 0.1 // log: 0 // value?

round.value = 2.5 // log: 3

It seems to me that "write-back" should probably be disabled, or needs to be turned on via options

If "write-back" is enabled, it should be the result of the round function

@zojize
Copy link
Contributor

zojize commented Jul 10, 2022

For associative binary operations (i.e. Math.min, Math.max, +, * etc.), I'm wondering if we could provide overloads for both array input and multiple arguments input?

e.g.

function min(arr: MaybeComputedRef<MaybeComputedRef<number>[]>): ComputedRef<number>
function min(...args: MaybeComputedRef<number>[]): ComputedRef<number>
function min(...args: [MaybeComputedRef<MaybeComputedRef<number>[]>] | MaybeComputedRef<number>[]): ComputedRef<number> {
  let xs: Ref<MaybeComputedRef<number>[]>
  if (args.length === 1 && Array.isArray(resolveUnref(args[0])))
    xs = resolveRef(args[0]) as typeof xs
  else
    xs = resolveRef(args) as typeof xs
  return computed(() => xs.value.reduce<number>((a, b) => Math.min(resolveUnref(a), resolveUnref(b)), Infinity))
}

min(ref(1), ref(2), 3) // works
min([1, 2, 3]) // works
min([ref(1), ref(2), 3]) // works
min(ref([1, 2, 3])) // works

@holazz holazz mentioned this issue Jul 10, 2022
9 tasks
@LittleSound
Copy link
Contributor

LittleSound commented Jul 10, 2022

@zojize I'm trying to achieve a similar effect in useSum
packages/math/useSum/index.test.ts

test('reactive list', () => {
  const list = reactive([10, 20])

  const sum = useSum(list)
  expect(sum.value).toBe(30)

  list[0] = 21
  expect(sum.value).toBe(41)

  list.push(5)
  expect(sum.value).toBe(46)
})

@yjl9903
Copy link
Contributor

yjl9903 commented Jul 12, 2022

@LittleSound I saw your implementation of useSum

export function useSum<T>(
list: MaybeComputedRef<MaybeComputedRef<T>[]>,
adder: ((...p: any[]) => any) = (sum, value) => sum + value,
...args: any[]
): ComputedRef<T> {
return computed(() => {
const reduce = Array.prototype.reduce.bind(resolveUnref(list))
const reduceCallback = (sum: any, value: any, index: number) => adder(resolveUnref(sum), resolveUnref(value), index)
// Depending on the behavior of reduce, undefined is also a valid initialization value,
// and this code will distinguish the behavior between them.
return args.length
? reduce(reduceCallback, args[0])
: reduce(reduceCallback)
})
}

Maybe we can have some more general array utilities, like useReduce, useFilter, useMap, useFind , useFindIndex, and so on ?

And then implement useMax, useMin, and useSum with useReduce ?


Another question is should we optimize useSum in advance, for the scenes that the input array is a long array of Ref<number>. We can store the sum, and modify the the sum when the value of a ref is changed. So it will not re-compute the full result at each time.

@antfu
Copy link
Member Author

antfu commented Jul 12, 2022

useReduce, useFilter, useMap, useFind , useFindIndex

Yes I already planned to have them in @vueuse/shared with the useArrayXX prefix. Pr welcome!

@LittleSound
Copy link
Contributor

Another question is should we optimize useSum in advance, for the scenes that the input array is a long array of Ref.

@yjl9903 Do you have any ideas about how to implement this kind of optimization?

@yjl9903
Copy link
Contributor

yjl9903 commented Jul 14, 2022

Another question is should we optimize useSum in advance, for the scenes that the input array is a long array of Ref.

@yjl9903 Do you have any ideas about how to implement this kind of optimization?

Maybe something like this

export function useSum(nums: Array<MaybeComputedRef<number>>) {
  const sum = ref(0)
  for (const num of nums) {
    sum.value += resolveUnref(num)
    watch(() => resolveUnref(num), (newNum, prevNum) => {
      sum.value += newNum - prevNum
    })
  }
  return sum
}

And I am not sure how to implement when wrapper array is also a ref.

Another problem is that the optimized one has different semantic from the origin. Counter example is

const r1 = ref(Number.MAX_VALUE)
const r2 = ref(Number.MAX_VALUE)
const sum = useSum([r1, r2]) // Inf
r1.value = 0
r2.value = 0
// NaN (Inf - Inf = NaN)

@LittleSound
Copy link
Contributor

  1. If the array itself is also responsive, perhaps add a watch(() => resolveUnref(nums).length, () => {}) as an iterator side effect to reinitialize when the length changes.
  2. Is Infinity a special edge case, and if so, can it be recognized and handled specially?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
help wanted Extra attention is needed pr welcome
Projects
None yet
Development

No branches or pull requests

4 participants