You have a fun question here. I just wrote about this recently so follow that link if you're interested in the ideas presented in this answer -
const randInt = (n = 0) =>
  Math.floor(Math.random() * n)
const { empty, map, concat } =
  Comparison
const sortByGroup =
  map(empty, x => x.group)
const sortByRand =
  map(empty, _ => randInt(3) - 1) // -1, 0, 1
Intuitively, we use map(empty, ...) to make a new comparison (sorter). concat is what we use to combine one comparison with another -
// sort by .group then sort by rand
const mySorter =
  concat(sortByGroup, sortByRand) 
Our comparison plugs directly into Array.prototype.sort -
const data =
  [ { name: "Alice", group: "staff" }
  , { name: "Monty", group: "client" }
  , { name: "Cooper", group: "client" }
  , { name: "Jason", group: "staff" }
  , { name: "Farrah", group: "staff" }
  , { name: "Celeste", group: "guest" }
  , { name: "Briana", group: "staff" }
  ]
console.log("first", data.sort(mySorter)) // shuffle once
console.log("second", data.sort(mySorter)) // shuffle again
In the output, we see items grouped by group and then randomised -
// first
[ { name: "Cooper", group: "client" }
, { name: "Monty", group: "client" }
, { name: "Celeste", group: "guest" }
, { name: "Alice", group: "staff" }
, { name: "Jason", group: "staff" }
, { name: "Farrah", group: "staff" }
, { name: "Briana", group: "staff" }
]
// second
[ { name: "Monty", group: "client" }
, { name: "Cooper", group: "client" }
, { name: "Celeste", group: "guest" }
, { name: "Farrah", group: "staff" }
, { name: "Alice", group: "staff" }
, { name: "Jason", group: "staff" }
, { name: "Briana", group: "staff" }
]
Finally, we implement Comparison -
const Comparison =
  { empty: (a, b) =>
      a < b ? -1
        : a > b ? 1
          : 0
  , map: (m, f) =>
      (a, b) => m(f(a), f(b))
  , concat: (m, n) =>
      (a, b) => Ordered.concat(m(a, b), n(a, b))
  }
const Ordered =
  { empty: 0
  , concat: (a, b) =>
      a === 0 ? b : a
  }
Expand the snippet below to verify the results in your own browser. Run the program multiple times to see the results are always ordered by group and then randomised -
const Comparison =
  { empty: (a, b) =>
      a < b ? -1
        : a > b ? 1
          : 0
  , map: (m, f) =>
      (a, b) => m(f(a), f(b))
  , concat: (m, n) =>
      (a, b) => Ordered.concat(m(a, b), n(a, b))
  }
const Ordered =
  { empty: 0
  , concat: (a, b) =>
      a === 0 ? b : a
  }
const randInt = (n = 0) =>
  Math.floor(Math.random() * n)
const { empty, map, concat } =
  Comparison
const sortByGroup =
  map(empty, x => x.group)
const sortByRand =
  map(empty, _ => randInt(3) - 1) // -1, 0, 1
const mySorter =
  concat(sortByGroup, sortByRand) // sort by .group then sort by rand
const data =
  [ { name: "Alice", group: "staff" }
  , { name: "Monty", group: "client" }
  , { name: "Cooper", group: "client" }
  , { name: "Jason", group: "staff" }
  , { name: "Farrah", group: "staff" }
  , { name: "Celeste", group: "guest" }
  , { name: "Briana", group: "staff" }
  ]
   
console.log(JSON.stringify(data.sort(mySorter))) // shuffle once
console.log(JSON.stringify(data.sort(mySorter))) // shuffle again
 
 
small improvement
Instead of hard-coding sorters like sortByGroup, we can make a parameterised comparison, sortByProp -
const sortByProp = (prop = "") =>
  map(empty, (o = {}) => o[prop])
const sortByFullName =
  concat
    ( sortByProp("lastName")  // primary: sort by obj.lastName
    , sortByProp("firstName") // secondary: sort by obj.firstName
    )
data.sort(sortByFullName) // ...
why a module?
The benefits of defining a separate Comparison module are numerous but I won't repeat them here. The module allows us to model complex sorting logic with ease -
const sortByName =
  map(empty, x => x.name)
const sortByAge =
  map(empty, x => x.age)
const data =
  [ { name: 'Alicia', age: 10 }
  , { name: 'Alice', age: 15 }
  , { name: 'Alice', age: 10 }
  , { name: 'Alice', age: 16 }
  ]
Sort by name then sort by age -
data.sort(concat(sortByName, sortByAge))
// [ { name: 'Alice', age: 10 }
// , { name: 'Alice', age: 15 }
// , { name: 'Alice', age: 16 }
// , { name: 'Alicia', age: 10 }
// ]
Sort by age then sort by name -
data.sort(concat(sortByAge, sortByName))
// [ { name: 'Alice', age: 10 }
// , { name: 'Alicia', age: 10 }
// , { name: 'Alice', age: 15 }
// , { name: 'Alice', age: 16 }
// ]
And effortlessly reverse any sorter. Here we sort by name then reverse sort by age -
const Comparison =
  { // ...
  , reverse: (m) =>
      (a, b) => m(b, a)
  }
data.sort(concat(sortByName, reverse(sortByAge)))
// [ { name: 'Alice', age: 16 }
// , { name: 'Alice', age: 15 }
// , { name: 'Alice', age: 10 }
// , { name: 'Alicia', age: 10 }
// ]
functional principles
Our Comparison module is flexible yet reliable. This allows us to write our sorters in a formula-like way - 
// this...
concat(reverse(sortByName), reverse(sortByAge))
// is the same as...
reverse(concat(sortByName, sortByAge))
And similarly with concat expressions -
// this...
concat(sortByYear, concat(sortByMonth, sortByDay))
// is the same as...
concat(concat(sortByYear, sortByMonth), sortByDay)
// is the same as...
nsort(sortByYear, sortByMonth, sortByDay)
multi-sort
Because our comparisons can be combined to create more sophisticated comparisons, we can effectively sort by an arbitrary number of factors. For example, sorting date objects requires three comparisons: year, month, and day. Thanks to functional principles, our concat and empty do all the hard work -
const Comparison =
  { // ...
  , nsort: (...m) =>
      m.reduce(Comparison.concat, Comparison.empty)
  }
const { empty, map, reverse, nsort } =
  Comparison
const data =
  [ { year: 2020, month: 4, day: 5 }
  , { year: 2018, month: 1, day: 20 }
  , { year: 2019, month: 3, day: 14 }
  ]
const sortByDate =
  nsort
    ( map(empty, x => x.year)  // primary: sort by year
    , map(empty, x => x.month) // secondary: sort by month
    , map(empty, x => x.day)   // tertiary: sort by day
    )
Now we can sort by year, month, day -
data.sort(sortByDate)
// [ { year: 2019, month: 11, day: 14 }
// , { year: 2020, month: 4, day: 3 }
// , { year: 2020, month: 4, day: 5 }
// ]
And just as easily reverse sort by year, month, day -
data.sort(reverse(sortByDate))
// [ { year: 2020, month: 4, day: 5 }
// , { year: 2020, month: 4, day: 3 }
// , { year: 2019, month: 11, day: 14 }
// ]
To run the reverse and nsort examples, follow along to the original post 
complex sort
You are certainly looking for a nuanced sorter, but worry not, our module is capable of handling it -
const { empty, map } =
  Comparison
const randParitionBy = (prop = "", m = new Map) =>
  map
    ( empty
    , ({ [prop]: value }) =>
        m.has(value)
          ? m.get(value)
          : ( m.set(value, Math.random())
            , m.get(value)
            )
    )
console.log(data)                               // presort...
console.log(data.sort(randParitionBy("group"))) // first...
console.log(data.sort(randParitionBy("group"))) // again...
Output - 
// pre-sort
[ {name:"Alice",group:"staff"}
, {name:"Monty",group:"client"}
, {name:"Cooper",group:"client"}
, {name:"Jason",group:"staff"}
, {name:"Farrah",group:"staff"}
, {name:"Celeste",group:"guest"}
, {name:"Briana",group:"staff"}
]
// first run (elements keep order, but sorted by groups, groups are sorted randomly)
[ {name:"Celeste",group:"guest"}
, {name:"Alice",group:"staff"}
, {name:"Jason",group:"staff"}
, {name:"Farrah",group:"staff"}
, {name:"Briana",group:"staff"}
, {name:"Monty",group:"client"}
, {name:"Cooper",group:"client"}
]
// second run (elements keep order and still sorted by groups, but groups are sorted differently)
[ {name:"Alice",group:"staff"}
, {name:"Jason",group:"staff"}
, {name:"Farrah",group:"staff"}
, {name:"Briana",group:"staff"}
, {name:"Monty",group:"client"}
, {name:"Cooper",group:"client"}
, {name:"Celeste",group:"guest"}
]
const Comparison =
  { empty: (a, b) =>
      a < b ? -1
        : a > b ? 1
          : 0
  , map: (m, f) =>
      (a, b) => m(f(a), f(b))
  }
  
const { empty, map } =
  Comparison
const data =
  [ { name: "Alice", group: "staff" }
  , { name: "Monty", group: "client" }
  , { name: "Cooper", group: "client" }
  , { name: "Jason", group: "staff" }
  , { name: "Farrah", group: "staff" }
  , { name: "Celeste", group: "guest" }
  , { name: "Briana", group: "staff" }
  ]
const randParitionBy = (prop = "", m = new Map) =>
  map
    ( empty
    , ({ [prop]: value }) =>
        m.has(value)
          ? m.get(value)
          : ( m.set(value, Math.random())
            , m.get(value)
            )
    )
console.log(JSON.stringify(data.sort(randParitionBy("group")))) // run multiple times!