1

I have this array of object;

let persons = [
    {id: 1, name: "..."},
    {id: 2, name: "..."},
    {id: 3, name: "..."},
    {id: 4, name: "..."},
    {id: 5, name: "..."},
    {id: 6, name: "..."},
    {id: 7, name: "..."},
    {id: 8, name: "..."}
]

I would like to split this array in two array of equal length. each time a execute the function which split the array It should return a random data in each array not the same list of object.

I try with this function

function splitArr(data, part) {
    let list1 = [];
    let list2 = [];
    for(let i = 0; i < data.length ; i++) {
        let random = Math.floor(Math.random() * data.length);
        if(random % 2 === 0) {
            list1.push(data[i]);
        } else {
            list2.push(data[i]);
        }

    }
    return [list1, list2];
}

It isn't obvious that the function will return exactly array of equal length each time. Some time it return array of 2 and 6 element not equal.

enter image description here

Yves Kipondo
  • 5,289
  • 1
  • 18
  • 31
  • For such a small dataset, `Math.random` isn't going to split it the way you want all the time. Why do you need the split to be "random"? – igg Jan 20 '20 at 13:45
  • No, this is only retrieving random element from an array, and use aproximaltly the same method I use in my function `Math.floor` and `Math.random` as I did in my code, But It doesn't split the array as I want – Yves Kipondo Jan 20 '20 at 13:48
  • I am working on a react Application, I have and array of object as above, and I want to split this array in two array, each time the root component is mount the two list of array must have random data no the same each time – Yves Kipondo Jan 20 '20 at 13:49
  • Use trincot's solution. – igg Jan 20 '20 at 13:51

6 Answers6

2

Just shuffle the array randomly and then splice the array in half.

For shuffling an array take the solution provided here.

function shuffle(a) {
    var j, x, i;
    for (i = a.length - 1; i > 0; i--) {
        j = Math.floor(Math.random() * (i + 1));
        x = a[i];
        a[i] = a[j];
        a[j] = x;
    }
    return a;
}

For getting the two lists from that, do:

let list2 = shuffle([...data]); // spread to avoid mutating the original
let list1 = list2.splice(0, data.length >> 1); 

The shift operator >> is used to get the truncated half of the array length.

trincot
  • 317,000
  • 35
  • 244
  • 286
  • thank you trincot, this work, just on problem with the **shift operator** I can't understand well how It work exactly – Yves Kipondo Jan 20 '20 at 14:10
  • It gives the same result as `Math.floor(data.length / 2)`. It is just to deal with the case where the length is odd. If you are absolutely sure it is even, then you can just do `data.length / 2`. – trincot Jan 20 '20 at 14:12
0

I think that fastest and more reliable would be to use native array methods in this case. I would recommend to go with slice method like below:

function splitArr(data) {
  const arrLength = data.length;

  const firstArr = data.slice(0, arrLength/2);
  const secArr = data.slice(arrLength / 2, arrLength);

  return [firstArr, secArr];
}

This way you got an universal function that will always return two arrays of same length. You can experiment with Math.min() and Math.ceil in edge cases (like with arrays of uneven lenght).

olejniczag
  • 384
  • 1
  • 10
0

You can do this with randojs.com really easily using the randoSequence function, which does not affect the original array. Then, use the slice function to split the arrays, and the bitwise operator >> to handle original arrays of odd length.

function shuffleAndSplit(arr){
  var shuffled = randoSequence(arr);
  shuffled.forEach((item, i) => {shuffled[i] = item.value;});
  return [shuffled.slice(0, shuffled.length >> 1), shuffled.slice(shuffled.length >> 1)];
}

console.log(shuffleAndSplit(["a", "b", "c", "d", "e", "f", "g"]));
<script src="https://randojs.com/1.0.0.js"></script>

This could be even simpler if you used the array map function, but that has some issues in Internet Explorer. If you don't care about IE, here's how you'd do this with map:

function shuffleAndSplit(arr){
  var shuffled = randoSequence(arr).map(item => item.value);
  return [shuffled.slice(0, shuffled.length >> 1), shuffled.slice(shuffled.length >> 1)];
}

console.log(shuffleAndSplit(["a", "b", "c", "d", "e", "f", "g"]));
<script src="https://randojs.com/1.0.0.js"></script>
Aaron Plocharczyk
  • 2,776
  • 2
  • 7
  • 15
-1

If you only wish to split it into two seperate part, you could use splice.

It takes two parameters, three if you wish to replace elements, the first one is the starting splice index. The second is the number of element to remove. The function will returns the removed element, spliting your array in half. And since the splice function is removing element from the original array, you will be left with two arrays of equals length ( if you have an even number of element ).

As for the randomess of your array, you could simply shuffle it before splitting it. Here i've used Jeff's answer

/**
 * https://stackoverflow.com/a/6274381/5784924
 * Shuffles array in place. ES6 version
 * @param {Array} a items An array containing the items.
 */
function shuffle(a) {
    for (let i = a.length - 1; i > 0; i--) {
        const j = Math.floor(Math.random() * (i + 1));
        [a[i], a[j]] = [a[j], a[i]];
    }
    return a;
}

let persons = shuffle([
    {id: 1, name: "..."},
    {id: 2, name: "..."},
    {id: 3, name: "..."},
    {id: 4, name: "..."},
    {id: 5, name: "..."},
    {id: 6, name: "..."},
    {id: 7, name: "..."},
    {id: 8, name: "..."}
]);

let firstArray = persons.splice(0, persons.length / 2);
console.log(firstArray.map((item) => item.id), persons.map((item) => item.id));
Nicolas
  • 8,077
  • 4
  • 21
  • 51
-1

the problem with your approach is this:

if(random % 2 === 0) {
    list1.push(data[i]);
} else {
    list2.push(data[i]);
}

you are trying to insert in a random array, but you are not handling if that array has the same length than the other ( and it will be hard to do, you will lose your random intentions)

it is better to insert random items in each one of the arrays, for each iteration.

let persons = [
    {id: 1, name: "..."},
    {id: 2, name: "..."},
    {id: 3, name: "..."},
    {id: 4, name: "..."},
    {id: 5, name: "..."},
    {id: 6, name: "..."},
    {id: 7, name: "..."},
    {id: 8, name: "..."}
]
function splitArr(data, part) {
    let list1 = [];
    let list2 = [];
    let isPair = false;
    
    while(data.length > 0){
      const randomEntry = Math.floor(Math.random() * data.length);
      const arrayToPush = isPair?list1:list2;
      arrayToPush.push(data[randomEntry]);
      data.splice(randomEntry, 1);
      isPair = !isPair;
    }
    console.log(list1.length, list2.length)
    return [list1, list2];
}

splitArr(persons)
Prince Hernandez
  • 3,623
  • 1
  • 10
  • 19
-1

Does this helps you ?

    function splitArr(data, count_decks) {
      data = data.slice()
      const decks = []
      let i = 0
      while (data.length) {
        if (!Array.isArray(decks[i])) decks[i] = []
        decks[i].push(data.splice(Math.random()*data.length, 1)[0])
        i = (i+1) % count_decks
      }
      return decks
    }

    splitArr(persons, 2) // here 2 for 2 decks
karkael
  • 431
  • 2
  • 9