Convert a single dimensional array into a multidimensional array - javascript

In Javascript, how to convert a single dimensional array into a multidimensional array of unspecified depth or length.
Example:
let input = ['a','b','b','b','a','a','b','b','b','c','c','a','a','b','b'];
const makeMatrix = () => {}
let output = makeMatrix(input);
// output: ['a',['b','b','b'],'a','a',['b','b','b',['c','c']],'a','a',['b','b']]
What should the makeMatrix function look like to accomplish this task? Assume that values always move in a linear forward direction, but might possibly cut backward. So a always leads to b. An a would never hop to c. But c might drop back down to a.
This is to try to convert heading elements into a table of contents. Making a simple single tier toc is easy, but making a multi tiered one is wracking my brain. I have looked through a number of solutions, but have not seen anything that solves this particular problem.

You could take a level variable and a levels array for pushing unknown elements.
var input = ['a', 'b', 'b', 'b', 'a', 'a', 'b', 'b', 'b', 'c', 'c', 'a', 'a', 'b', 'b'],
levels = [[]],
level = 0,
result;
input.forEach(v => {
var l = level;
do {
if (levels[l][0] === v) {
level = l;
levels[level].push(v);
return;
}
} while (l--)
levels[level].push(levels[level + 1] = [v]);
level++;
});
result = levels[0][0];
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

The dumb eval solution I had in mind, if it's what you wanted it can be made neat...
function toMulti(arr) {
let str = "[";
let level = 1;
const charLevels = { a: 1, b: 2, c: 3 };
arr.forEach(char => {
const charLevel = charLevels[char];
if (level < charLevel) {
for (let i = 0; i < charLevel - level; i++) {
str += "[";
}
}
if (level > charLevel) {
for (let i = 0; i < level - charLevel; i++) {
str += "],";
}
}
level = charLevel;
str += `'${char}',`;
});
for (let i = 0; i < level; i++) {
str += "]";
}
return eval(str);
}

Alternative version, using JSON building/parsing:
const input = ['a', 'b', 'b', 'b', 'a', 'a', 'b', 'b', 'b', 'c', 'c', 'a', 'a', 'b', 'b'];
const result = JSON.parse(Object.entries(input).reduce((json, [key, val]) => {
const jsonVal = JSON.stringify(val);
const diff = key > 0 ? val.charCodeAt(0) - input[key - 1].charCodeAt(0) : 0;
if (diff > 0) {
json += ',['.repeat(diff) + jsonVal;
} else if (diff < 0) {
json += ']'.repeat(-diff) + ',' + jsonVal;
} else {
json += (key > 0 ? ',' : '') + jsonVal;
}
return json;
}, '[') + ']'.repeat(input.slice(-1)[0].charCodeAt(0) - input[0].charCodeAt(0) + 1));
console.log(result);
This basically builds a JSON string using Array.reduce on the input array, adding each item and comparing key codes to include the right amount of opening/closing brackets in the process.

Related

Js secret santa claus algoritm

I wanted to make a small script in js that having a list of users, one user has to make a gift to another.
By applying the following constraints:
If "a" is the santa claus and gives a gift to "c" it cannot be the other way around.
So "c" cannot be the santa claus of "a".
It must work with both an even and an odd number of users.
In your opinion, what could be the right approach to use to try to minimize the number of comparisons, that is, speed up the script.
I was thinking something like this to start, but afterwards I'm not sure how to proceed:
let name = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
let a = [...name];
let group1 = [];
let groupSanta = [];
let groupUser = [];
for (var i = 0; i < name.length / 2 - 1; i++) {
let santaClaus = a[Math.floor(Math.random() * a.length)];
a = a.filter(item => item !== santaClaus);
let user = a[Math.floor(Math.random() * a.length)];
a = a.filter(item => item !== user);
group1.push({ santaClaus, user });
}
console.log(a, group1);
You can just randomly sort the array and assign each person to the next one. Then assign the first person to the last one in the array
// Define names
const names = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
// Function to shuffle array
const shuffle = (arr) => {
for (let i = arr.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[arr[i], arr[j]] = [arr[j], arr[i]];
}
return arr;
}
const randomNames = shuffle(names);
// Match each person with the next one, folding over at the end
const matches = randomNames.map((name, index) => {
return {
santa: name,
receiver: randomNames[index + 1] || randomNames[0],
}
});
console.log(matches);
let players = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
players = shuffleArray(players)
const matches = players.map((name, index) => {
return {
santa: name,
receiver: players[index + 1] || players[0],
}
});
function shuffleArray(array) {
let currentIndex = array.length, randomIndex
while (currentIndex != 0) {
randomIndex = Math.floor(Math.random() * currentIndex)
currentIndex--
[array[currentIndex], array[randomIndex]] = [array[randomIndex], array[currentIndex]]
}
return array
}
console.log(matches)

How to modify my code to get all non-unique values?

With duplicates, this code worked fine
const findDuplicates = (word) => {
let arr = word.toLowerCase();
let sorted_arr = [...arr].slice().sort();
console.log(sorted_arr);
let results = [];
for (let i = 0; i < sorted_arr.length - 1; i++) {
if (sorted_arr[i + 1] == sorted_arr[i]) {
results.push(sorted_arr[i]);
}
}
return results;
}
console.log(findDuplicates('piccdda123dd'));
Output
[
'1', '2', '3', 'a',
'c', 'c', 'd', 'd',
'd', 'd', 'i', 'p'
]
[ 'c', 'd', 'd', 'd' ]
How to modify if condition to deal with multiple non-unique values?
For something like this we should be gunning for constant time 0(n). Sorting is going to give us O(nlogn) at best and is not required.
function findDuplicates(s) {
const seen = {};
const result = [];
for (let c of s.toLowerCase()) {
if(c in seen) {
result.push(c);
}
seen[c] = true; // could be any value doesn't have to be boolean.
}
return result;
}
console.log(findDuplicates('piccdda123dd'));
result
[ 'c', 'd', 'd', 'd' ]
We just need to iterate over the string once and keep a map object (could use es6 Set) that tells us if we've seen this value before. If we see it again and it's already been seen we can append it to the result array.
If you wanted to see each duplicated character only once try
function findDuplicates2(s) {
const seen = {};
for (let c of s.toLowerCase()) {
const ent = seen[c];
seen[c] = (ent === undefined) ? 1 : ent + 1;
}
return Object.entries(seen).filter(([k,v]) => v > 1).map(([k,v]) => k);
}
console.log(findDuplicates2('piccdda123dd'));
// prints ['c', 'd']
SOLVED.
const findDuplicates = (word) => {
let arr = word.toLowerCase();
let sorted_arr = [...arr].slice().sort();
console.log(sorted_arr);
let results = [];
for (let i = 0; i < sorted_arr.length - 1; i++) {
if (sorted_arr[i + 1] == sorted_arr[i]) {
if (!results.includes(sorted_arr[i]))
results.push(sorted_arr[i]);
}
}
return results;
}
ES6 includes IS REALLY powerfull.

JavaScript Replace matching members of array compared to other array

I got this task and I can't get how to replace the matches with the string "BUSTED", can you help me please. I'm new to JS. Most probably there is a way more elegant way to do this. Any help appreciated.
You're given two arrays: one that holds every member of fCommunity and another one
that holds every possible suspect.
Replace every fCommunity member from the Suspect list with the word "BUSTED"
var fCommunityMembers = ['A','B','C'];
var SuspectList = ['F','X','B','Z','Y','C','ZS','D','K','M','N'];
I managed to retrieve the matching members but how do I replace them in the suspect list?:
Array.prototype.diff = function(SuspectList) {
var ret = [];
this.sort();
SuspectList.sort();
for(var i = 0; i < this.length; i += 1) {
if(SuspectList.indexOf( this[i] ) > -1){
ret.push( this[i] );
}
}
return ret;
};
var ListOfMatches = Array.from(fCommunityMembers.diff(SuspectList));
console.log( ListOfMatches );
Use Array.prototype.map() to iterate fCommunityMembers. If an member is found in the SuspectList return BUSTED, if not return the member:
var fCommunityMembers = ['A','B','C'];
var SuspectList = ['F','X','B','Z','Y','C','ZS','D','K','M','N'];
var result = fCommunityMembers.map(function(member) {
return SuspectList.indexOf(member) !== -1 ? 'BUSTED' : member;
});
console.log(result);
What you need is a function that can do the intersection between two arrays
filter can help in your case
var fCommunityMembers = [
'A',
'B',
'C',
'D',
'F'
];
var SuspectList = [
'F',
'X',
'B',
'Z',
'Y',
'C',
'ZS',
'D',
'L'
];
var suspects= fCommunityMembers.filter(function(el){
return SuspectList.indexOf(el)>-1;
});
Edit #1
To replace the values by busted, do the following:
var suspectsFnc = function (fc, sus) {
var busted = fCommunityMembers.filter(function (el) {
return SuspectList.indexOf(el) > -1;
});
for (var i = 0; i < fc.length; i++) {
if (busted.indexOf(fc[i]) > -1) {
fc[i] = "Busted";
}
}
return fc;
}
var suspects = suspectsFnc(fCommunityMembers, SuspectList);
Judt use filter like this :
var ListOfMatches = SuspectList.filter(x => fCommunityMembers.indexOf(x) !== -1);
and a forEach loop to insert BUSTED instead of previous matched elements
fCommunityMembers.forEach((x,i,arr) => {
if (SuspectList.indexOf(x) !== -1)
arr[i] = "BUSTED";
});

there is an string array A , and an string array B . I want to delete elements in A which are not in B

I think i messed somewhere, Here is my code.
var flag;
for (i = 0; i < A.length; i++)
{
flag = 0;
for (j = 0; j < B.length; j++)
{
if (A[i].indexOf(B[j]) != -1)
{
flag = 1;
}
}
if (flag == 0)
{
A.splice(i, 1);
}
}
It gives output not as per my need
Someone please Help me out
I would do the job like this;
//returns intersection of multiple arrays
Array.prototype.intersect = function(...a) {
return [this,...a].reduce((p,c) => p.filter(e => c.includes(e)));
};
var a = [0,1,2,3,4,5],
b = [4,5,6,7,8,9];
a = a.intersect(b);
console.log(a);
You could use a function which generates first an object with all characters as properties and take it as hashtable for the filtering of array1.
function deleteSome(array1, array2) {
var o = Object.create(null);
array2.forEach(function (a) {
o[a] = true;
});
return array1.filter(function (a) {
return this[a];
}, o);
}
var a = 'abcdefgh'.split(''),
b = 'banana'.split('');
console.log(deleteSome(a,b));
Technically, array "a" should have only elements which are present in array "b".
var a = [1,2,3,4];
var b = [4,5,6];
var new_a = [];
a.map(function(v,i,a){
if(b.indexOf(v) !== -1){
new_a.push(v);
}
});
console.log(new_a); //[4]
By this way i can filter as many arrays as you want.
var a = ['A', 'A', 'R', 'S', 'M', 'D', 'E']
var b = ['C', 'X', 'D', 'F']
//you can add as many arrays as you want
/*var c = ['O', 'P', 'D', 'Q']
var d = ['R', 'D', 'D', 'Z']*/
var arrays = [a,b, /*c , d */];
var result = arrays.shift().filter(function(v) {
return arrays.every(function(a) {
return a.indexOf(v) !== -1;
});
});
console.log(JSON.stringify(result));

Combining some array items

I'm kind of new to functional programming and I try to do the following: Let's say I have an array of values: ['a','b-','c'] and I want that every item which ends with a '-' is merged with the following array entry: ['a','b-c'].
Of course I can do this by making a for-loop:
var test = ['a', 'b-', 'c'], result = [];
for (var i=0;i<test.length;i++) {
var curr = test[i];
if (curr.endsWith('-')) {
curr += test[i+1];
i++;
}
result.push(curr);
}
But how can one do this without the for loop?
To be honest, the way you programmed is probably the most efficient way to do this.
However, here's another option:
var test = ['a', 'b-', 'c'],
result = test.join().replace(/-,/g, '').split(',');
console.log(result);
This joins all elements into a string: 'a,b-,c', removes all occurrences of '-,' > 'a,bc', then splits the string back up into an array with the desired result, as you can see in the output.
This can be fool-proofed a bit, by changing the separator used in the join / split:
var test = ['a', 'b-', 'c'],
separator = '||',
result = test.join(separator)
.replace(new RegExp('-' + separator, 'g'), '')
.split(separator);
One possible approach (with .reduce):
var arr = ['a', 'b-', 'c'];
var trans = arr.reduce(function(acc, cur) {
if (acc.length && acc[acc.length - 1].slice(-1) === '-') {
acc[acc.length - 1] += cur;
}
else {
acc.push(cur);
}
return acc;
}, []);
This can also be achieved using Array.prototype.map:
var test = ['a', 'b-', 'c'];
var result = test.slice().map(function (x, i, a) {
if (x.endsWith("-") && a[i+1]) {
var r = x + a[i+1]; // Join this and the next element in the array
a.splice(i, 1); // Remove the next element from the array
return r;
}
return x;
}).filter(function (x) {
return typeof x !== 'undefined';
}); // Since the array comes back with a different length and some undefined elements, remove those. Thanks #Cerbrus for pointing this out
console.log(test, result, result.length); // ["a", "b-", "c"] ["a", "b-c"] 2
This way will work for multiple dashed elements in a row, and if the last element has a dash, uses Array.forEach
var test = ['a', 'b-', 'c-'], result = [], next = "";
test.forEach(function(curr) {
if (curr.endsWith('-')) {
next += curr;
if (curr == test[test.length-1]) {
result.push(next);
}
}else {
result.push(next + curr);
next = "";
}
});
document.write(result);
Another map + filter one. Most likely slower, as filter add's another iteration through the array, but works as the original does (which is probably not what the OP wants when there are multiple -'s in a row).
var test = ['a', 'b-', 'c-', 'd', 'e'], result = [];
result = test
.map((curr, i, array) => (curr.endsWith('-') && array[i + 1] !== undefined) ? curr + array[i+1] : curr)
.filter((curr, i, arr) => (i>0 && arr[i-1].length > 1 && curr.length === 1) ? false : true)
document.write(result);
Didn't read all answers, so sry if I repeat sth. that has already been said.
Functional programming doesn't mean that there is always a predefined function that does exactly what you intend to; or a combination of some.
Sometimes it is better to write a simple short utility-function than abusing the ones that are already there.
How about some "problems" like multiple dashed-values next to each other, or at the end of the list? How do you want to handle these cases?
This would be my implementation:
function combineDashedStrings(arr){
var result = [], pending = ""
for (var i=0; i<arr.length; i++) {
var curr = pending + arr[i];
pending = curr.endsWith("-") && curr || ""; //multiple concats
//pending = !pending && curr.endsWith("-") && curr || ""; //single concat
pending || result.push(curr);
}
//pending && result.push(curr); //add last item if it contains a dash
return result
}
combineDashedStrings(['a', 'b-', 'c-', 'd', 'e-']);
feel free to switch the commented lines/options

Categories

Resources