Related
var arr1 = [1,2,3,4,5];
var arr2 = ["a","b","c","d","e"];
Let's assume that I want to create a list like
1 a
2 b
3 c
4 d
5 e
by using template literal.
let x;
x = document.createElement('li');
x.innerHTML += `<span>${<arr1 index>}</span> <span>${<arr2 index>}</span>`
How can I do that ? Can we use forEach for two arrays in same time ?
This would be more like flatten(zip(arr1, arr2)). There is no built-in zip though you can very easily make it and you can see Array.flat here: MDN: Array.flat.
const arr1 = [1, 2, 3, 4, 5];
const arr2 = ["a", "b", "c", "d", "e"];
const flatten = arr => arr.flat();
const zip = (a, b) => a.map((e, idx) => [e, b[idx]]);
const arr3 = flatten(zip(arr1, arr2));
console.log(arr3);
The answer is "kind of." What you can do is loop through one array with a forEach method, and use the optional argument index to get the value of the second array as well. Something like this:
var arr1 = [1,2,3,4,5];
var arr2 = ["a","b","c","d","e"];
arr1.forEach((value, index) => {
console.log(value);
console.log(arr2[index])
})
But if the data in the two arrays are at all related, you'd want to put the data in the same object, like this:
var arr = [
{
num: 1,
letter: "a"
},
{
num: 2,
letter: "b"
},
{
num: 3,
letter: "c"
}
];
arr.forEach(value => {
console.log(value.num);
console.log(value.letter);
})
Or you would want to use a regular for loop
You could simply use a for() loop instead:
const max = Math.max(arrA.length, arrB.length)
for (let i = 0; i < max; i++) {
const objA = arrA[i],
objB = arrB[i]
if ('undefined' !== typeof objA) {
console.log({ objA })
}
if ('undefined' !== typeof objB) {
console.log({ objB })
}
}
There is no real magic here. You use an index variable, and let it increment:
var arr1 = [1,2,3,4,5];
var arr2 = ["a","b","c","d","e"];
let ul = document.querySelector("ul");
for (let i = 0; i < arr1.length; i++) {
let li = document.createElement('li');
for (let val of [arr1[i], arr2[i]]) {
let span = document.createElement('span');
span.textContent = val;
li.appendChild(span);
}
ul.appendChild(li);
}
<ul></ul>
There are of course other ways to loop, like with forEach, but it comes down to the same principle.
BTW, don't use string literals (template literals) for combining HTML with content, as you might have < or & characters in the content, which really should be escaped. In some cases, not escaping those may lead to unexpected side effects. By creating the elements with createElement and assigning content to their textContent or innerText properties, you avoid those potential issues. Some libraries make it possible to do this with less code, in a more functional way.
As to the initial data: in object oriented languages, like JavaScript, it is better practice to put related values together in one object. In the example, 1 and "a" apparently have a connection, so -- if possible -- you should define the initial data structure as something like this:
var data = [
{ value: 1, name: "a" },
{ value: 2, name: "b" },
{ value: 3, name: "c" },
{ value: 4, name: "d" },
{ value: 5, name: "e" }
];
In javascript I want to create an array of 20 objects containing 2 random numbers between 1 and 250. All numbers in the array I want to be unique from each other. Basically like this:
const matches = [
{ player1: 1, player2: 2 },
{ player1: 3, player2: 4 },
{ player1: 5, player2: 6 },
{ player1: 7, player2: 8 },
...
]
// all unique numbers
I have found this other method
const indexes = [];
while (indexes.length <= 8) {
const index = Math.floor(Math.random() * 249) + 1;
if (indexes.indexOf(index) === -1) indexes.push(index);
}
But this only returns an array of numbers:
[1, 2, 3, 4, 5, 6, 7, 8, ...]
You could use Array.from method to create an array of objects and then also create custom function that will use while loop and Set to generate random numbers.
const set = new Set()
function getRandom() {
let result = null;
while (!result) {
let n = parseInt(Math.random() * 250)
if (set.has(n)) continue
else set.add(result = n)
}
return result
}
const result = Array.from(Array(20), () => ({
player1: getRandom(),
player2: getRandom()
}))
console.log(result)
You can create an array of 251 elements (0-250) and preset all values to 0 to keep track of the generated elements. Once a value is generated, you mark that value in the array as 1.
Check below:
// create an array of 251 elements (0-250) and set the values to 0
let array = Array.from({ length: 251 }, () => 0);
let matches = [];
function getRandomUniqueInt() {
// generate untill we find a value which hasn't already been generated
do {
var num = Math.floor(Math.random() * 249) + 1;
} while(array[num] !== 0);
// mark the value as generated
array[num] = 1;
return num;
}
while (matches.length <= 4) {
let obj = { "player1" : getRandomUniqueInt(), "player2" : getRandomUniqueInt() };
matches.push(obj);
}
console.log(matches);
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.
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.
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).
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>
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));
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)
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
I have a SQL with 4 columns: salesman_name | sales | goal | month
The goal column has an unique value for each month and the sales column is the sum of the sales in that month.
With jQuery I put these values into 3 different arrays:
$(resultset).each(function (i, el) {
arraySalesman.push(el.salesman_name);
arraySales.push(el.sales);
arrayGoal.push(el.goal);
});
When I filter my dataset for x months, i have x values for sales and x values for goals for each salesman. I need to reduce those values to an unique sales and an unique goal value for each salesman (accumulated value).
I've tried something like that on JavaScript:
var dataObjSales = arraySalesman.reduce((t, c, i) => (t[c] = (t[c] || 0) + arraySales[i], t), {});
var dataObjGoal = arraySalesman.reduce((t, c, i) => (t[c] = (t[c] || 0) + arrayGoal[i], t), {});
It returns to me something like that:
console.log("dataObjSales", dataObjSales);
dataObjSales
{…}
"Salesman1": 1650574.98
"Salesman2": 829888.27
"Salesman3": 1186831.46
"Salesman3": 1342287.91
"Salesman4": 3185057.64
"Salesman5": 387644.28
"Salesman6": 2262157.5
<prototype>: Object { … }
I need to transform this into an object with 3 properties: Obj.salesman, Obj.sales and Obj.goals. Can I do it into the first reduce? Or can I transform these 2 reduces and the salesman_name array into an object with 3 properties?
I believe you can make good use of a Map:
const arraySalesperson = ["John", "Alice", "Bob", "John", "John", "Alice"];
const arraySales = [100, 420, 138, 89, 74, 86];
const arrayGoals = [1, 2, 3, 4, 5, 6];
// create a map
const resultsBySalesperson = new Map();
// traverse the list of salespersons
for (let i = 0; i < arraySalesperson.length; i++) {
const name = arraySalesperson[i];
// see if it already exists in the map
let salesperson = resultsBySalesperson.get(name);
if (!salesperson) {
// if not, let's create an object now
salesperson = {
name: name,
sales: 0,
goals: 0
};
// store it in the map
resultsBySalesperson.set(name, salesperson);
}
// update the object
salesperson.sales += arraySales[i];
salesperson.goals += arrayGoals[i];
}
// here you have the map ready with both sales and goal properly accumulated
console.info([...resultsBySalesperson.entries()]);
For example, if I have these arrays:
var name = ["Bob","Tom","Larry"];
var age = ["10", "20", "30"];
And I use name.sort() the order of the "name" array becomes:
var name = ["Bob","Larry","Tom"];
But, how can I sort the "name" array and have the "age" array keep the same order? Like this:
var name = ["Bob","Larry","Tom"];
var age = ["10", "30", "20"];
You can sort the existing arrays, or reorganize the data.
Method 1:
To use the existing arrays, you can combine, sort, and separate them:
(Assuming equal length arrays)
var names = ["Bob","Tom","Larry"];
var ages = ["10", "20", "30"];
//1) combine the arrays:
var list = [];
for (var j = 0; j < names.length; j++)
list.push({'name': names[j], 'age': ages[j]});
//2) sort:
list.sort(function(a, b) {
return ((a.name < b.name) ? -1 : ((a.name == b.name) ? 0 : 1));
//Sort could be modified to, for example, sort on the age
// if the name is the same. See Bonus section below
});
//3) separate them back out:
for (var k = 0; k < list.length; k++) {
names[k] = list[k].name;
ages[k] = list[k].age;
}
This has the advantage of not relying on string parsing techniques, and could be used on any number of arrays that need to be sorted together.
Method 2: Or you can reorganize the data a bit, and just sort a collection of objects:
var list = [
{name: "Bob", age: 10},
{name: "Tom", age: 20},
{name: "Larry", age: 30}
];
list.sort(function(a, b) {
return ((a.name < b.name) ? -1 : ((a.name == b.name) ? 0 : 1));
});
for (var i = 0; i<list.length; i++) {
alert(list[i].name + ", " + list[i].age);
}
For the comparisons,-1 means lower index, 0 means equal, and 1 means higher index. And it is worth noting that sort() actually changes the underlying array.
Also worth noting, method 2 is more efficient as you do not have to loop through the entire list twice in addition to the sort.
http://jsfiddle.net/ghBn7/38/
Bonus Here is a generic sort method that takes one or more property names.
function sort_by_property(list, property_name_list) {
list.sort((a, b) => {
for (var p = 0; p < property_name_list.length; p++) {
prop = property_name_list[p];
if (a[prop] < b[prop]) {
return -1;
} else if (a[prop] !== a[prop]) {
return 1;
}
}
return 0;
});
}
Usage:
var list = [
{name: "Bob", age: 10},
{name: "Tom", age: 20},
{name: "Larry", age: 30},
{name: "Larry", age: 25}
];
sort_by_property(list, ["name", "age"]);
for (var i = 0; i<list.length; i++) {
console.log(list[i].name + ", " + list[i].age);
}
Output:
Bob, 10
Larry, 25
Larry, 30
Tom, 20
You could get the indices of name array using Array.from(name.keys()) or [...name.keys()]. Sort the indices based on their value. Then use map to get the value for the corresponding indices in any number of related arrays
const indices = Array.from(name.keys())
indices.sort( (a,b) => name[a].localeCompare(name[b]) )
const sortedName = indices.map(i => name[i]),
const sortedAge = indices.map(i => age[i])
Here's a snippet:
const name = ["Bob","Tom","Larry"],
age = ["10", "20", "30"],
indices = Array.from(name.keys())
.sort( (a,b) => name[a].localeCompare(name[b]) ),
sortedName = indices.map(i => name[i]),
sortedAge = indices.map(i => age[i])
console.log(indices)
console.log(sortedName)
console.log(sortedAge)
This solution (my work) sorts multiple arrays, without transforming the data to an intermediary structure, and works on large arrays efficiently. It allows passing arrays as a list, or object, and supports a custom compareFunction.
Usage:
let people = ["john", "benny", "sally", "george"];
let peopleIds = [10, 20, 30, 40];
sortArrays([people, peopleIds]);
[["benny", "george", "john", "sally"], [20, 40, 10, 30]] // output
sortArrays({people, peopleIds});
{"people": ["benny", "george", "john", "sally"], "peopleIds": [20, 40, 10, 30]} // output
Algorithm:
Create a list of indexes of the main array (sortableArray)
Sort the indexes with a custom compareFunction that compares the values, looked up with the index
For each input array, map each index, in order, to its value
Implementation:
/**
* Sorts all arrays together with the first. Pass either a list of arrays, or a map. Any key is accepted.
* Array|Object arrays [sortableArray, ...otherArrays]; {sortableArray: [], secondaryArray: [], ...}
* Function comparator(?,?) -> int optional compareFunction, compatible with Array.sort(compareFunction)
*/
function sortArrays(arrays, comparator = (a, b) => (a < b) ? -1 : (a > b) ? 1 : 0) {
let arrayKeys = Object.keys(arrays);
let sortableArray = Object.values(arrays)[0];
let indexes = Object.keys(sortableArray);
let sortedIndexes = indexes.sort((a, b) => comparator(sortableArray[a], sortableArray[b]));
let sortByIndexes = (array, sortedIndexes) => sortedIndexes.map(sortedIndex => array[sortedIndex]);
if (Array.isArray(arrays)) {
return arrayKeys.map(arrayIndex => sortByIndexes(arrays[arrayIndex], sortedIndexes));
} else {
let sortedArrays = {};
arrayKeys.forEach((arrayKey) => {
sortedArrays[arrayKey] = sortByIndexes(arrays[arrayKey], sortedIndexes);
});
return sortedArrays;
}
}
See also https://gist.github.com/boukeversteegh/3219ffb912ac6ef7282b1f5ce7a379ad
If performance matters, there is sort-ids package for that purpose:
var sortIds = require('sort-ids')
var reorder = require('array-rearrange')
var name = ["Bob","Larry","Tom"];
var age = [30, 20, 10];
var ids = sortIds(age)
reorder(age, ids)
reorder(name, ids)
That is ~5 times faster than the comparator function.
It is very similar to jwatts1980's answer (Update 2).
Consider reading Sorting with map.
name.map(function (v, i) {
return {
value1 : v,
value2 : age[i]
};
}).sort(function (a, b) {
return ((a.value1 < b.value1) ? -1 : ((a.value1 == b.value1) ? 0 : 1));
}).forEach(function (v, i) {
name[i] = v.value1;
age[i] = v.value2;
});
You are trying to sort 2 independet arrays by only calling sort() on one of them.
One way of achieving this would be writing your own sorting methd which would take care of this, meaning when it swaps 2 elements in-place in the "original" array, it should swap 2 elements in-place in the "attribute" array.
Here is a pseudocode on how you might try it.
function mySort(originals, attributes) {
// Start of your sorting code here
swap(originals, i, j);
swap(attributes, i, j);
// Rest of your sorting code here
}
inspired from #jwatts1980's answer, and #Alexander's answer here I merged both answer's into a quick and dirty solution;
The main array is the one to be sorted, the rest just follows its indexes
NOTE: Not very efficient for very very large arrays
/* #sort argument is the array that has the values to sort
#followers argument is an array of arrays which are all same length of 'sort'
all will be sorted accordingly
example:
sortMutipleArrays(
[0, 6, 7, 8, 3, 4, 9],
[ ["zr", "sx", "sv", "et", "th", "fr", "nn"],
["zero", "six", "seven", "eight", "three", "four", "nine"]
]
);
// Will return
{
sorted: [0, 3, 4, 6, 7, 8, 9],
followed: [
["zr", th, "fr", "sx", "sv", "et", "nn"],
["zero", "three", "four", "six", "seven", "eight", "nine"]
]
}
*/
You probably want to change the method signature/return structure, but that should be easy though. I did it this way because I needed it
var sortMultipleArrays = function (sort, followers) {
var index = this.getSortedIndex(sort)
, followed = [];
followers.unshift(sort);
followers.forEach(function(arr){
var _arr = [];
for(var i = 0; i < arr.length; i++)
_arr[i] = arr[index[i]];
followed.push(_arr);
});
var result = {sorted: followed[0]};
followed.shift();
result.followed = followed;
return result;
};
var getSortedIndex = function (arr) {
var index = [];
for (var i = 0; i < arr.length; i++) {
index.push(i);
}
index = index.sort((function(arr){
/* this will sort ints in descending order, change it based on your needs */
return function (a, b) {return ((arr[a] > arr[b]) ? -1 : ((arr[a] < arr[b]) ? 1 : 0));
};
})(arr));
return index;
};
I was looking for something more generic and functional than the current answers.
Here's what I came up with: an es6 implementation (with no mutations!) that lets you sort as many arrays as you want given a "source" array
/**
* Given multiple arrays of the same length, sort one (the "source" array), and
* sort all other arrays to reorder the same way the source array does.
*
* Usage:
*
* sortMultipleArrays( objectWithArrays, sortFunctionToApplyToSource )
*
* sortMultipleArrays(
* {
* source: [...],
* other1: [...],
* other2: [...]
* },
* (a, b) => { return a - b })
* )
*
* Returns:
* {
* source: [..sorted source array]
* other1: [...other1 sorted in same order as source],
* other2: [...other2 sorted in same order as source]
* }
*/
export function sortMultipleArrays( namedArrays, sortFn ) {
const { source } = namedArrays;
if( !source ) {
throw new Error('You must pass in an object containing a key named "source" pointing to an array');
}
const arrayNames = Object.keys( namedArrays );
// First build an array combining all arrays into one, eg
// [{ source: 'source1', other: 'other1' }, { source: 'source2', other: 'other2' } ...]
return source.map(( value, index ) =>
arrayNames.reduce((memo, name) => ({
...memo,
[ name ]: namedArrays[ name ][ index ]
}), {})
)
// Then have user defined sort function sort the single array, but only
// pass in the source value
.sort(( a, b ) => sortFn( a.source, b.source ))
// Then turn the source array back into an object with the values being the
// sorted arrays, eg
// { source: [ 'source1', 'source2' ], other: [ 'other1', 'other2' ] ... }
.reduce(( memo, group ) =>
arrayNames.reduce((ongoingMemo, arrayName) => ({
...ongoingMemo,
[ arrayName ]: [
...( ongoingMemo[ arrayName ] || [] ),
group[ arrayName ]
]
}), memo), {});
}
You could append the original index of each member to the value, sort the array, then remove the index and use it to re-order the other array. It will only work where the contents are strings or can be converted to and from strings successfuly.
Another solution is keep a copy of the original array, then after sorting, find where each member is now and adjust the other array appropriately.
I was having the same issue and came up with this incredibly simple solution. First combine the associated ellements into strings in a seperate array then use parseInt in your sort comparison function like this:
<html>
<body>
<div id="outPut"></div>
<script>
var theNums = [13,12,14];
var theStrs = ["a","b","c"];
var theCombine = [];
for (var x in theNums)
{
theCombine[x] = theNums[x] + "," + theStrs;
}
var theSorted = theAr.sort(function(a,b)
{
var c = parseInt(a,10);
var d = parseInt(b,10);
return c-d;
});
document.getElementById("outPut").innerHTML = theS;
</script>
</body>
</html>
How about:
var names = ["Bob","Tom","Larry"];
var ages = ["10", "20", "30"];
var n = names.slice(0).sort()
var a = [];
for (x in n)
{
i = names.indexOf(n[x]);
a.push(ages[i]);
names[i] = null;
}
names = n
ages = a
Simplest explantion is the best, merge the arrays, and then extract after sorting:
create an array
name_age=["bob#10","Tom#20","Larry#30"];
sort the array as before, then extract the name and the age, you can use # to reconise where
name ends and age begins. Maybe not a method for the purist, but I have the same issue and this my approach.