I have a quiz. Here is the array of questions. The program is supposed to loop through them in order. However, it is first going through all the arrays with numbers and then the ones with letters, as opposed to the order I put them in. Here are the questions:
allQuestions = {
'358670': ['358670', '967723', '214571', '569420', '630697', 0],
'mbdpf': ['ajcwh', 'xirgb', 'dzelv', 'mbdpf', 'xguqx', 3],
'637592': ['348335', '921186', '637592', '188551', '391500', 2],
'xhtjv': ['xhtjv', 'jneez', 'cthul', 'bulwl', 'kqfwc', 0],
'206471': ['206471', '419643', '366549', '871328', '926142', 0],
'mwdif': ['bzvai', 'kslgq', 'futgf', 'mwdif', 'sikyp', 3],
'980924': ['327151', '242777', '708582', '860616', '980924', 4],
'usiyi': ['iyfod', 'lapwg', 'dqmtt', 'dyvwk', 'usiyi', 4],
'768898': ['808547', '689143', '875754', '768898', '872606', 3],
'ziojg': ['xqdiv', 'cyqsu', 'akoed', 'obtpn', 'ziojg', 4]
};
Here's the codepen and here's the entire JS:
window.onload = function() {
var questionArea = document.getElementsByClassName('questions')[0],
answerArea = document.getElementsByClassName('answers')[0],
checker = document.getElementsByClassName('checker')[0],
current = 0,
allQuestions = {
'358670': ['358670', '967723', '214571', '569420', '630697', 0],
'mbdpf': ['ajcwh', 'xirgb', 'dzelv', 'mbdpf', 'xguqx', 3],
'637592': ['348335', '921186', '637592', '188551', '391500', 2],
'xhtjv': ['xhtjv', 'jneez', 'cthul', 'bulwl', 'kqfwc', 0],
'206471': ['206471', '419643', '366549', '871328', '926142', 0],
'mwdif': ['bzvai', 'kslgq', 'futgf', 'mwdif', 'sikyp', 3],
'980924': ['327151', '242777', '708582', '860616', '980924', 4],
'usiyi': ['iyfod', 'lapwg', 'dqmtt', 'dyvwk', 'usiyi', 4],
'768898': ['808547', '689143', '875754', '768898', '872606', 3],
'ziojg': ['xqdiv', 'cyqsu', 'akoed', 'obtpn', 'ziojg', 4]
};
function loadQuestion(curr) {
var question = Object.keys(allQuestions)[curr];
questionArea.innerHTML = '';
questionArea.innerHTML = question;
}
function loadAnswers(curr) {
var answers = allQuestions[Object.keys(allQuestions)[curr]];
answerArea.innerHTML = '';
for (var i = 0; i < answers.length -1; i += 1) {
var createDiv = document.createElement('div'),
text = document.createTextNode(answers[i]);
createDiv.appendChild(text);
createDiv.addEventListener("click", checkAnswer(i, answers));
answerArea.appendChild(createDiv);
}
}
function checkAnswer(i, arr) {
return function () {
var givenAnswer = i,
correctAnswer = arr[arr.length-1];
if (givenAnswer === correctAnswer) {
addChecker(true);
} else {
addChecker(false);
}
if (current < Object.keys(allQuestions).length -1) {
current += 1;
loadQuestion(current);
loadAnswers(current);
} else {
questionArea.innerHTML = 'Done!';
answerArea.innerHTML = '';
}
};
}
function addChecker(bool) {
var createDiv = document.createElement('div'),
txt = document.createTextNode(current + 1);
createDiv.appendChild(txt);
if (bool) {
createDiv.className += 'correct';
checker.appendChild(createDiv);
} else {
createDiv.className += 'false';
checker.appendChild(createDiv);
}
}
loadQuestion(current);
loadAnswers(current);
};
You're iterating over an object, and not over an array. The order of object keys is not fixed - Use an array if you need a specific order:
var obj = {
a: 'a',
1: 1,
x: 'x'
}
console.log('object keys', Object.keys(obj))
var arr = ['a', 1, 'x']
console.log('array values', arr)
Javascript objects provide no guarantees about the order of the elements. The order that you originally put the keys will not necessarily be the order that they are enumerated.
There are 2 main ways to approach this.
Create an array separate from the object with the keys in the correct order.
var keys = ['358670', 'mbdpf', '637592', 'xhtjv', '206471', 'mwdif', '980924', 'usiyi', '768898'];`
Use an array to store all of the data instead of an object.
allQuestions = [
{ key: '358670', data: ['358670', '967723', '214571', '569420', '630697', 0] },
{ key: 'mbdpf', data: ['ajcwh', 'xirgb', 'dzelv', 'mbdpf', 'xguqx', 3] },
// etc.
};
Objects in JavaScript, like dictionaries in most other languages, do not guarantee order! Arrays do.
Here's how you can wrap your data into an array:
allQuestions = [
{ 'name': '358670', 'contents': ['358670', '...'] },
{ 'name': 'mbdpf', 'contents': ['...'] }
];
Related
I need to remove similar duplicates as well as real duplicates from 2D array in JavaScript.
let a = [
[5, 6],
[1,1],
[6,5],
[1,1],
[3,2],
[2,3]
]
function makeUnique(arr) {
var uniques = [];
var itemsFound = {};
for(var i = 0, l = arr.length; i < l; i++) {
var stringified = JSON.stringify(arr[i]);
if(itemsFound[stringified]) continue;
uniques.push(arr[i]);
itemsFound[stringified] = true;
}
return uniques;
}
a=makeUnique(a)
console.log(a);
I have got this output:
[ [ 5, 6 ], [ 1, 1 ], [ 6, 5 ], [ 3, 2 ], [ 2, 3 ] ]
Correct should be:
[ [ 5, 6 ], [ 1, 1 ], [ 2, 3 ] ]
My code removes correctly duplicates, but I need to remove similar duplicates also.
For example if I have [3,2] and [2,3] I should remove [3,2] (the one which has bigger starting index value.)
Could you help me to fix this?
Here is an example of how you can do it:
function makeUnique(arr) {
var uniques = [];
var itemsFound = {};
arr.sort((a, b) => a[0] + a[1] - (b[0] + b[1]))
for (var i = 0, l = arr.length; i < l; i++) {
if (!itemsFound[arr[i]] && !itemsFound[[arr[i][1], arr[i][1]]]) {
uniques.push(arr[i]);
itemsFound[arr[i]] = true;
itemsFound[[arr[i][1], arr[i][0]]] = true;
}
}
return uniques;
}
I hope it helps.
There are two parts
similar should be considered
among similar, one with smaller first key should stay
1. Similar should be considered
Here you can just make the key for hashmap in such a way that similar items produce same key.
One way to do that is sort the items in the tuple and then form the key, as there are two items only, first one will be min and second one will be max
let a = [
[5, 6],
[1,1],
[6,5],
[1,1],
[3,2],
[2,3]
]
function makeUnique(arr) {
var uniques = [];
var itemsFound = {};
for(var i = 0, l = arr.length; i < l; i++) {
let [a,b] = arr[i];
const hashKey = [ Math.min(a,b), Math.max(a,b)];
var stringified = JSON.stringify(hashKey);
if(itemsFound[stringified]) continue;
uniques.push(arr[i]);
itemsFound[stringified] = true;
}
return uniques;
}
let ans1=makeUnique(a)
console.log(ans1);
2. Among similar, the one with smaller first key should stay
Now you can remember in the hashmap what the value for a key was and keep updating it based on the correct candidate
let a = [
[5, 6],
[1,1],
[6,5],
[1,1],
[3,2],
[2,3]
]
function makeUniqueSmallerFirst(arr) {
var items = {};
for(var i = 0, l = arr.length; i < l; i++) {
let [a,b] = arr[i];
const hashKey = [ Math.min(a,b), Math.max(a,b)];
var stringified = JSON.stringify(hashKey);
if (stringified in items) {
let previous = items[stringified];
if (previous[0] > arr[i][0]) {
items[stringified] = arr[i];
}
} else {
items[stringified] = arr[i] // I am just storing the array because if I see a similar item next time, I can compare if that has first item smaller or not
}
}
return Object.values(items); // this doesn't guarantee output order though
// if you want order as well . you can iterate over input array once more and arrange the items in the preferred order.
}
let ans2=makeUniqueSmallerFirst(a)
console.log(ans2);
UPDATED (More simple and faster example for ES5+):
function makeUnique(arr) {
return new Set(a.map(
arr => JSON.stringify(arr.sort((a, b) => a - b)))
)
}
const m = makeUnique(a)
console.log(m) //
OLD:
This is an example of code that makes a two-dimensional array with arrays of any length unique.
let a = [
[5, 6],
[1, 1],
[6, 5],
[1, 5],
[3, 2],
[2, 3],
[6, 5, 3],
[3, 5, 6]
]
function isUnique(uniqueArray, checkedArray) {
let checked = [...checkedArray];
let unique = [...uniqueArray];
let uniqueValue = 0;
unique.forEach(value => {
if (checked.includes(value)) {
checked.splice(checked.indexOf(value), 1)
} else uniqueValue++;
})
return uniqueValue > 0;
}
function makeUnique(array2d) {
let unique = [array2d[0]]
array2d.forEach(checkedArray => {
if (unique.some(uniqueArray => {
if (checkedArray.length !== uniqueArray.length) return false;
return !isUnique(uniqueArray, checkedArray)
}
)) return 0;
else unique.push(checkedArray)
})
return unique
}
console.log(makeUnique(a)) // [ [ 5, 6 ], [ 1, 1 ], [ 1, 5 ], [ 3, 2 ], [ 6, 5, 3 ] ]
isUnique() this function checks if the numbers in both arrays are unique, and if they are, it outputs true. We use the copy through spread operator, so that when you delete a number from an array, the array from outside is not affected.
makeUnique() function makes the array unique, in the following way:
It checks if our unique two-dimensional array has at least one array that is identical to checkedArray
The first check if the arrays are of different lengths - they are unique, skip and check for uniqueness, if !isUnique gives out true, then the array is skipped by return 0
I am trying to remove duplicates from my 2D array but no success.
Here is what I am doing any idea where am making mistake?
function Remove_duplicates_from_2d_array(data_array) {
if (data_array.length > 0) {
let unique_index_counter = 0;
// loop on target array
for (var a = 0; a < data_array.length; a++) {
var unique_array = data_array[unique_index_counter];
if (a === unique_index_counter) {
continue;
}
console.log('comparing index: ' + a + ' ( ' + data_array[a] + ' ) -- index: ' + a + ' ( ' + data_array[a] + ' )');
if (data_array[a].sort().join(',') === unique_array.sort().join(',')) {
console.log('match it index ' + a + ' - ' + unique_index_counter);
// same arrays
data_array.splice(a, 1);
a = 0; // reset for loop as splice will rebuilt array
}
// a will be equal to data_array length incase there is no match found
if (a === data_array.length) {
unique_index_counter++;
}
if(unique_index_counter != data_array.length) {
a = 0; // reset for loop because we have not checked all items
}
}
return data_array;
} else {
return [];
}
}
var a1 = [1, 2, 3];
b1 = [4, 4, 5];
c1 = [3, 4, 5];
d1 = [4, 4, 5];
var data_array = [];
data_array.push(a1);
data_array.push(b1);
data_array.push(c1);
data_array.push(d1);
console.log('original array.');
console.log(data_array);
var r = Remove_duplicates_from_2d_array(data_array);
console.log('unique array.');
console.log(r); // [[1,2,3],[4,4,5],[3,4,5]]
You could take a Set for seen sorted string and filter the array by checking the occurences.
function removeDuplicates(array) {
var seen = new Set;
return array.filter(a => (s => !seen.has(s) && seen.add(s))(a.join()));
}
console.log(removeDuplicates([[1, 2, 3], [4, 4, 5], [3, 4, 5], [4, 4, 5]]));
// [[1, 2, 3], [4, 4, 5], [3, 4, 5]]
.as-console-wrapper { max-height: 100% !important; top: 0; }
With an object.
function removeDuplicates(array) {
var seen = {};
return array.filter(v => !seen[v] && (seen[v] = true));
}
console.log(removeDuplicates([[1, 2, 3], [4, 4, 5], [3, 4, 5], [4, 4, 5]]));
// [[1, 2, 3], [4, 4, 5], [3, 4, 5]]
.as-console-wrapper { max-height: 100% !important; top: 0; }
Sets are nice and all, but you may benefit from seeing a simple for-based approach too:
var orig = [[1, 2, 3],
[4, 4, 5],
[3, 4, 5],
[4, 4, 5]];
function undupe(arr){
let ret=[];
for(let candidate of arr){
let found=false;
for(let line of ret){
let linematch=true;
for(let i=0;i<line.length;i++){
if(candidate[i]!==line[i]){
linematch=false; // if one element does not match, the lines are different
break; // and the rest does not matter
}
}
if(linematch){
found=true; // if all elements matched, candidate is a duplicate
break; // remaining lines do not matter
}
}
if(!found){
ret.push(candidate); // candidate is unique, add it to the result
}
}
return ret;
}
console.log(undupe(orig));
candidate loop gets all items in the input array
line loop gets all items from ret array, so the items which are unique so far
i loop compares the actual numbers (one from candidate with the corresponding one from line)
these loop things are slow at large data sizes, so it is a good idea to terminate them as soon as possible. Like when the first elements do not match in a pair of lines, they are different for sure, there is no need to check the rest.
Of course it could be more structured, like
var orig = [[1, 2, 3],
[4, 4, 5],
[3, 4, 5],
[4, 4, 5]];
function linesmatch(line1,line2){
for(let i=0;i<line1.length;i++)
if(line1[i]!==line2[i])
return false;
return true;
}
function isdupe(arr,line){
for(let l of arr)
if(linesmatch(l,line))
return true;
return false;
}
function undupe(arr){
let ret=[];
for(let candidate of arr)
if(!isdupe(ret,candidate))
ret.push(candidate);
return ret;
}
console.log(undupe(orig));
This would be the more textbook-like variant with small, readable functions built on top of each other.
You may Array.prototype.reduce() source array doing Array.prototype.find()'ing duplicates along the way:
const src = [[1,2,3],[4,4,5],[3,4,5],[4,4,5]],
arrsAreEqual = (a1,a2) => a1.sort().join('|') === a2.sort().join('|'),
dedupe = src.reduce((r,a) =>
(!r.find(ra => arrsAreEqual(a,ra)) && r.push(a), r), [])
console.log(JSON.stringify(dedupe))
.as-console-wrapper{min-height:100%;}
here is my fixed simple version of duplicate remover based on for loop
function Remove_duplicates_from_2d_array(data_array) {
var unique_array = [];
if (data_array.length > 0) {
let unique_index_counter = 0;
// loop on target array
for (var a = 0; a < data_array.length; a++) {
// if have same indexs , skip
if (a === unique_index_counter) {
continue;
}
// compare both indexes
if (data_array[unique_index_counter].join(',') == data_array[a].join(',') ) {
data_array.splice(a, 1); // remove that a index
a = 0; // reset for loop as splice will rebuilt array
continue;
}
// a will be equal to data_array length incase there is no match found
else if ( (data_array.length != 0 && a == data_array.length - 1) ) {
unique_array.push(data_array[unique_index_counter]); // push unique index to unique array
data_array.splice(unique_index_counter, 1); // remove that a index
a = 0; // reset for loop as splice will rebuilt array
}
} // for end
// by now one unique element will be still left in source arrays
unique_array.push(data_array[0]); // push unique index to unique array
return unique_array;
} else {
return [];
}
}
var a1 = [1, 2, 3];
b1 = [4, 4, 5];
c1 = [3, 4, 5];
d1 = [4, 4, 5];
var data_array = [];
data_array.push(a1);
data_array.push(b1);
data_array.push(c1);
data_array.push(d1);
console.log('original array.');
console.log(data_array);
var r = Remove_duplicates_from_2d_array(data_array);
console.log('unique array.');
console.log(r); // [[1,2,3],[4,4,5],[3,4,5]]
If I have the following array of arrays:
var myArr = [[0, 1, 2], [1, 2, 6], [9, 10], [10, 11], [11, 12], [13]];
How can I merge the arrays that share at least one common value to produce the following output?
var myMergedArr = [[0, 1, 2, 6], [9, 10, 11, 12], [13]];
Thanks!
NOTE: They won't always be nicely ordered and the shared values may not always be the starting/ending values when everything is ordered. I have ordered the above values for clarity.
The array can be reduced with an empty array (merged) as the starting value. For each array in myArray, existing is defined as an array of subArrays of merged such that the intersection of each subArray and array is not empty.
If no such array could be found, existing will remain undefined and a new array (contained in another array) is being defined as existing and pushed to merged.
If multiple matches are found (existing.slice(1) is not empty), they need to be merged together: existing[0] acts as the container where all the other sub-arrays (existing[1..]) get merged (without duplicates). Then those further matches need to be found in merged and removed, as they have been merged already. This guarantees multiple arrays to be merged if they belong together, even if they weren’t merged earlier.
Then, each item of array is pushed to existing[0] if it isn’t included yet. Finally, merged is returned. Then the next iteration of reduce can take merged again as the first argument and continue with the next array in myArr.
This is the ES6 code for that. You can transpile and polyfill it to ES5, if you need to.
var myArr = [
[0, 1, 2],
[1, 2, 6],
[9, 10],
[10, 11],
[11, 12],
[13]
],
myMergedArr = myArr.reduce((merged, array) => {
let existing = merged.filter((subArray) => subArray.filter((subItem) => array.includes(subItem)).length);
if (!existing.length) {
existing = [
[]
];
merged.push(existing[0]);
}
else {
existing.slice(1).forEach((furtherArray) => {
furtherArray.forEach((item) => {
if (!existing[0].includes(item)) {
existing[0].push(item);
}
});
merged.splice(merged.findIndex((subArray) => furtherArray == subArray), 1);
});
}
array.forEach((item) => {
if (!existing[0].includes(item)) {
existing[0].push(item);
}
});
return merged;
}, []);
console.log(myMergedArr);
This second snippet is the same code but with a changed array. This is to demonstrate that this script will work, even if the sub-arrays aren’t in order: first [0, 1, 2] is on its own, then [3, 4, 5] is also on its own — both still separated. Only later [2, 3] causes all previous arrays to merge into one.
var myArr = [
[0, 1, 2],
[3, 4, 5],
[2, 3],
[7, 9],
[9, 10],
[13]
],
myMergedArr = myArr.reduce((merged, array) => {
let existing = merged.filter((subArray) => subArray.filter((subItem) => array.includes(subItem)).length);
if (!existing.length) {
existing = [
[]
];
merged.push(existing[0]);
}
else {
existing.slice(1).forEach((furtherArray) => {
furtherArray.forEach((item) => {
if (!existing[0].includes(item)) {
existing[0].push(item);
}
});
merged.splice(merged.findIndex((subArray) => furtherArray == subArray), 1);
});
}
array.forEach((item) => {
if (!existing[0].includes(item)) {
existing[0].push(item);
}
});
return merged;
}, []);
console.log(myMergedArr);
A disjoint-set data structure seems perfect for your case:
function merge(arrays) {
var ds = new DisjointSet();
arrays.forEach(function(array) {
array.reduce(function(prevSet, currentValue) {
var currentSet = ds.getSet(currentValue);
if(prevSet) prevSet.mergeWith(currentSet);
return currentSet;
}, false);
});
return ds.partitions();
}
var myArr = [[0, 1, 2], [1, 2, 6], [9, 10], [10, 11], [11, 12], [13]];
console.log(JSON.stringify(merge(myArr)));
<script> /* DisjointSet library */
class MySet {
constructor(owner) {
this.rank = 0;
this.parent = this;
this.owner = owner;
}
representative() {
var parent = this.parent;
if(this === parent) return this;
while(parent !== (parent = parent.parent));
this.parent = parent; /* Path compression */
return parent;
}
mergeWith(other) {
var r1 = this.representative(),
r2 = other.representative();
if(r1 === r2) return;
if(r1.owner !== r2.owner) throw new Error("Can't merge");
if(r1.rank > r2.rank) { r2.parent = r1; return; }
r1.parent = r2;
if(r1.rank === r2.rank) ++r1.rank;
}
}
class DisjointSet {
constructor() {
this.sets = new Map();
}
getSet(value) {
var sets = this.sets;
var set = sets.get(value);
if(set) return set;
set = new MySet(this);
sets.set(value, set);
return set;
}
partitions() {
var parts = new Map();
for(var [value,set] of this.sets) {
var repre = set.representative();
var arr = parts.get(repre);
if(arr) arr.push(value);
else parts.set(repre, [value]);
}
return [...parts.values()];
}
}
</script>
Assuming constant map operations, the amortized time cost should be only O(n α(n)) ≈ O(n).
the amortized time per operation is only O(α(n)), where α(n) [...]
is less than 5 for all remotely practical values of n. Thus, the
amortized running time per operation is effectively a small constant.
Note I used ES6 maps to be able to associate each value with its set. This is not needed if all values are numbers, you could then store them as object properties. But in partitions you will need to extract the value associated with a set, and storing that data will require more memory.
function merge(arrays) {
var ds = new DisjointSet();
arrays.forEach(function(array) {
array.reduce(function(prevSet, currentValue) {
var currentSet = ds.getSet(currentValue);
if(prevSet) prevSet.mergeWith(currentSet);
return currentSet;
}, false);
});
return ds.partitions();
}
var myArr = [[0, 1, 2], [1, 2, 6], [9, 10], [10, 11], [11, 12], [13]];
console.log(JSON.stringify(merge(myArr)));
<script> /* DisjointSet library */
function MySet(value, owner) {
this.rank = 0;
this.parent = this;
this.value = value;
this.owner = owner;
}
MySet.prototype.representative = function() {
var parent = this.parent;
if(this === parent) return this;
while(parent !== (parent = parent.parent));
this.parent = parent; /* Path compression */
return parent;
};
MySet.prototype.mergeWith = function(other) {
var r1 = this.representative(),
r2 = other.representative();
if(r1 === r2) return;
if(r1.owner !== r2.owner) throw new Error("Can't merge");
if(r1.rank > r2.rank) { r2.parent = r1; return; }
r1.parent = r2;
if(r1.rank === r2.rank) ++r1.rank;
};
function DisjointSet() {
this.sets = Object.create(null);
}
DisjointSet.prototype.getSet = function(value) {
var sets = this.sets;
var set = sets[value];
if(set) return set;
set = new MySet(value, this);
sets[value] = set;
return set;
};
DisjointSet.prototype.partitions = function() {
var parts = [];
var assoc = Object.create(null);
var sets = this.sets;
Object.getOwnPropertyNames(sets).forEach(function(value) {
var set = sets[value];
var repreValue = set.representative().value;
var arr = assoc[repreValue];
if(arr) arr.push(set.value);
else parts.push(assoc[repreValue] = [set.value]);
});
return parts;
};
</script>
I believe the problem can be solved with a fairly simple functional piece of code as follows;
function merger(arr){
return arr.map((e,i,a) => a.slice(i)
.reduce((p,c) => e.some(n => c.includes(n)) ? [...new Set([...p,...c])] : p,[]))
.reduce((r,s) => { var merged = false;
r = r.map(a => a.some(n => s.includes(n)) ? (merged = true, [...new Set([...a,...s])]) : a);
!merged && r.push(s);
return r;
},[]);
}
var arr1 = [[0, 1, 2], [1, 2, 6], [9, 10], [10, 11], [11, 12], [13]],
arr2 = [[0, 1], [2, 3], [1, 2]];
console.log(merger(arr1));
console.log(merger(arr2));
A little explanation on the critical part. Using Set object hand to hand with Array object is very easy with the help of spread operator. So the following piece might look a little confusing but it does an important job.
[...new Set([...a,...s])]
Let's assume a is an array or set and s is another array or set. Then [...a,...s] makes an array of the two merged into. new Set([...a,...s]) makes a new set by removing the dupes in the merged array. [...new Set([...a,...s])] turns our set back into an array of a and b concatenated and dupes removed. Cool..!
a.some(n => s.includes(n))
If array a and array s have at least one common item returns true else false.
Have an array arr =
[ [1,2] , [5,1], [23,24], [3,4], [22,23], [2,3]]
output needed = [[5,1,2,3,4],[22,23,24]]
tried using concat, reduce methods of array in javascript but could not achieve the desired result - any ideas?
I'm going to start by saying the code example and description is not very clear, but I'll take a shot...
It seems you have 3 requirements:
Flatten the original array.
Filter out duplicates.
Group values (somehow?) within the flattened array.
The first 2 tasks on the list can be accomplished using reduce, concat, filter and indexOf methods.
var multiArray = [ [1,2] , [5,1], [23,24], [3,4], [22,23], [2,3] ];
var flattened = multiArray.reduce(function (prev, curr) {
return prev.concat(curr);
}, []);
var unique = flattened.filter(function (element, index, array) {
return array.indexOf(element) === index;
});
However, I wont be able to give you an example for that last task until you make it clear how you're hoping to have these values grouped within the flattened array.
I'll update my original answer once clarified.
This is another solution yet i believe i can make it with less code and more functional. I don't enjoy using forEach() or flags like reduced much. This one recursively encodes the array items until there is nothing left to encode. Later I will give it another try to make it look more sleek.
var rawarr = [[1,2], [5,1], [23,24], [3,4], [22,23], [2,3]],
encodarr = (arr) => {
var reduced = false;
arr.forEach((c,i,a) => {
var sa = a.slice(i+1),
j = sa.findIndex(f => c[0] == f[f.length-1] || c[c.length-1] == f[0]);
arr[i] = !!~j ? c[0] == sa[j][sa[j].length-1] ? (reduced = true, a.splice(i+j+1,1)[0].concat(c.slice(1)))
: (reduced = true, c.slice(0,-1).concat(a.splice(i+j+1,1)[0]))
: c;
return arr;
});
return reduced ? encodarr(arr) : arr;
};
document.write("<pre>" + JSON.stringify(encodarr(rawarr)) + "</pre>");
Basically this problem can be solved with more loops and only one array (A) or with an object as reference and only one loop (B).
A
An in situ proposal with no object, but with more loops. It checks every array with the path information with each other. If a match is found, the two arrays are
var data = [[1, 2], [5, 1], [23, 24], [3, 4], [22, 23], [2, 3], [4, 5]],
result = function (data) {
function first(a) { return a[0]; }
function last(a) { return a[a.length - 1]; }
var i = 0, j, t;
outer: while (i < data.length - 1) {
j = i + 1;
while (j < data.length) {
t = data.splice(j, 1)[0];
if (first(data[i]) === last(t)) {
t.pop();
data[i] = t.concat(data[i]);
i = 0;
continue outer;
}
if (last(data[i]) === first(t)) {
t.shift();
data[i] = data[i].concat(t);
i = 0;
continue outer;
}
data.push(t);
j++;
}
i++
}
return data;
}(data);
document.write('<pre>' + JSON.stringify(result, 0, 4) + '</pre>');
B
This is a single loop proposal which takes all parts and appends them if possible. Later all start arrays are returned as result.
var data = [[1, 2], [5, 1], [23, 24], [3, 4], [22, 23], [2, 3], [4, 5]],
result = function (data) {
var o = { start: {}, end: {} };
data.forEach(function (a) {
var temp;
if (o.end[a[0]]) {
if (o.start[a[1]] && a[0] !== o.start[a[1]][o.start[a[1]].length - 1] && a[1] !== o.end[a[0]][0]) {
temp = o.end[a[0]].concat(o.start[a[1]]);
o.start[temp[0]] = temp;
o.end[temp[temp.length - 1]] = temp;
delete o.start[a[1]];
} else {
o.end[a[1]] = o.end[a[0]];
o.end[a[1]].push(a[1]);
}
delete o.end[a[0]];
return;
}
if (o.start[a[1]]) {
o.start[a[0]] = o.start[a[1]];
o.start[a[0]].unshift(a[0]);
delete o.start[a[1]];
return;
}
temp = a.slice();
o.start[a[0]] = temp;
o.end[a[1]] = temp;
});
return Object.keys(o.start).map(function (k) { return o.start[k]; });
}(data);
document.write('<pre>' + JSON.stringify(result, 0, 4) + '</pre>');
I'm not sure if the title of this question is correct or not and also not sure what the appropriate keyword to search on google.
I have an array look like:
var myArray = [1,1,2,2,2,3,4,4,4];
and I want to sort my array into:
var myArray = [1,2,3,4,1,2,4,2,4];
Please in to my expected result. the order is ascending but duplicate value will repeated on last sequence instead of put it together in adjacent keys. So the expected result grouped as 1,2,3,4 1,2,4 and 2,4.
Thank you for your help and sorry for my bad English.
This code works. But it may exist a better solution.
// We assume myArray is already sorted
var myArray = [1,1,2,2,2,3,4,4,4],
result = [];
while (myArray.length) {
var value = myArray.shift();
// Find place
var index = 0;
while(result[index] && result[index][result[index].length - 1] == value) index++;
if(!result[index]) {
result[index] = [];
}
result[index][result[index].length] = value;
}
result.reduce(function(current, sum) {
return current.concat(sum);
});
console.log(result) // Display [1, 2, 3, 4, 1, 2, 4, 2, 4]
Here is my method using JQuery and it does not assume the array is already sorted.
It will iterate through the array and no duplicates to tempResultArray, once finished, it will then add them to the existing result and repeat the process again to find duplicates.
This is not the most efficient method, but it can be handled by one function and does not require the array to be sorted.
var myArray = [1,1,2,2,2,3,4,4,4],result = [];
while (myArray && myArray.length) {
myArray = customSort(myArray);
}
console.log(result);
function customSort(myArray){
var tempResultArray = [], tempMyArray = [];
$.each(myArray, function(i, el){
if($.inArray(el, tempResultArray ) === -1){
tempResultArray.push(el);
}else{
tempMyArray.push(el);
}
});
tempResultArray.sort(function(a, b){return a-b});
$.merge( result,tempResultArray)
return tempMyArray;
}
JSFiddle
This proposal features a straight forward approach with focus on array methods.
function sprout(array) {
return array.reduce(function (r, a) {
!r.some(function (b) {
if (b[b.length - 1] < a) {
b.push(a);
return true;
}
}) && r.push([a]);
return r;
}, []).reduce(function (r, a) {
return r.concat(a);
});
}
document.write('<pre>' + JSON.stringify(sprout([1, 1, 2, 2, 2, 3, 4, 4, 4]), 0, 4) + '</pre>');
document.write('<pre>' + JSON.stringify(sprout([1, 2, 3, 7, 7, 7]), 0, 4) + '</pre>');
document.write('<pre>' + JSON.stringify(sprout([1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 6, 6, 6, 7]), 0, 4) + '</pre>');
here's another solution:
var myArray = [1, 1, 2, 2, 2, 3, 4, 4, 4];
function mySequelArray(arr) {
var res = arguments[1] || [];
var nextVal;
var min = Math.min.apply(null, arr);
if (res.length > 0) {
nextVal = arr.filter(function (x) {
return x > res[res.length - 1]
}).sort()[0] || min;
} else {
nextVal = min;
}
res.push(nextVal);
arr.splice(arr.indexOf(nextVal), 1);
return (arr.length > 0) ? mySequelArray(arr, res) : res;
}
console.log(mySequelArray(myArray))
fiddle
My best approach will be to split your array into separate arrays for each repeated value, then arrange each separate array and join altogether.
UPDATED:
I wrote a quick code sample that should work if the same number in inputArray is not given more than twice. You could improve it by making it recursive thus creating new arrays for each new number and removing the limitation. Had some free time so i re-wrote a recursive function to sort any given array in sequence groups like you wanted. Works like a charm, inputArray does not need to be sorted and doesn't require any libraries. Jsfiddle here.
var inputArray = [3, 4, 1, 2, 3, 1, 2, 4, 1, 2, 5, 1];
var result = sortInSequence(inputArray);
console.log(result); //output: [1, 2, 3, 4, 5, 1, 2, 3, 4, 1, 2, 1]
function sortInSequence(inputArray){
var inputArraySize = inputArray.length,
tempArray = [], //holds new array we are populating
sameValuesArray = [], //holds same values that we will pass as param in recursive call
rSorted = []; //init sorted array in case we have no same values
for(var i = inputArraySize; i > 0; i--){
var value = inputArray.pop();
tempArray.push(value);
var counter = 0,
tempArraySize = tempArray.length;
for(var j = 0; j < tempArraySize; j++){
if(tempArray[j] == value){
counter++;
}
}
if(counter == 2){
//value found twice, so remove it from tempArray and add it in sameValuesArray
var sameValue = tempArray.pop();
sameValuesArray.push(sameValue);
}
}
if(sameValuesArray.length > 0){
rSorted = sortInSequence(sameValuesArray);
}
tempArray.sort();
return tempArray.concat(rSorted);
}