Function to display every element of array with each other element once - javascript

How do I display every element in an Array with each other element once, but not itself?
For example:
var myArray:any = "a b c d"
And then display:
a,b
a,c
a,d
b,a
b,c
b,d
etc.

A for in for works fine.
var myArray = "a b c d".split(' ');
for (let first of myArray) {
for (let second of myArray) {
if (first !== second) {
console.log(`${first},${second}`)
}
}
}

Try
function f (arr) {
arr.forEach(function(e) {
arr.forEach(function(e2) {
if (e === e2) return;
var str = e + "," + e2;
// print str
});
});
}
f("a b c d".split(' '));

You could also use dankogai/js-combinatorics like this:
cmb = Combinatorics.combination(['a','b','c','d'], 2);
while(a = cmb.next()) console.log(a);
// ["a", "b"]
// ["a", "c"]
// ["a", "d"]
// ["b", "c"]
// ["b", "d"]
// ["c", "d"]

"a b c d".split(' ').map((el, idx, arr)=>{
let elIdx = arr.indexOf(el);
let rest = arr.slice(0,elIdx).concat(arr.slice(elIdx+1)).map((l)=> console.log(`${el},${l}`) );
});

for this you can do imbrication of map fonctions, but you do have to work with arrays
here an exemple:
const myArray = ["a", "b", "c", "d", "e", "f", "g"]
// forEach accept one first param x current value
// second param is optional xi index of value
// third param is optional too and it refer the array itself
myArray.forEach( (x, xi, myArr) => {
myArr.forEach( (y, yi) => {
if(xi !== yi) { console.log(`${x}, ${y}`); }
});
});

Related

Look into an array how many values are the same

So I trying to make a function for a slotmachine in javascript to look at 2 arrays and return a score on based on how many values are the same.
let testArray = ["a", "b", "c"];
let resultArray = ["a", "a", "c"];
let scoreCounter = 0;
let score = 0;
for (let i = 0; i < testArray.length; i++) {
for (let y = 0; y < resultArray.length; y++) {
if (resultArray[y] == testArray[i]) {
scoreCounter++
}
}
if (scoreCounter == 2) {
score = 200
} else if (scoreCounter == 3) {
score = 300
};
}
console.log(score)
// Return 3 times good => a,a,c are in the array.
I am trying to get this result:
If the player spins the wheel and get 0 same matches he gets 0 points
2 same matches = 200 points
3 same matches = 300 points
let testArray = ["a", "b", "c"];
let resultArray = ["a", "b", "c"]; // 0 points
let testArray = ["a", "b", "c"];
let resultArray = ["a", "a", "c"]; // 200 points
let testArray = ["a", "b", "c"];
let resultArray = ["a", "a", "a"]; // 300 points
How can I fix my loops to check for this result?
Group the array into an object whose keys are the values found, and whose values are the number of times a value has been found. Then call Math.max on the values of that object to get the maximum number of occurrences of any one element:
let resultArray = ["a", "a", "c"];
const grouped = {};
for (const item of resultArray) {
grouped[item] = (grouped[item] || 0) + 1;
}
const maxSameVals = Math.max(...Object.values(grouped));
const score = maxSameVals === 1
? 0
: maxSameVals * 100;
console.log(score);
You could count the values and then find the count by looking to the first array.
function getScore(left, right) {
let counts = {},
i = 0;
while (i < right.length) {
counts[right[i]] = (counts[right[i]] || 0) + 1;
i++;
}
return (counts[left.find(k => counts[k] > 1)] || 0) * 100;
}
console.log(getScore(["a", "b", "c"], ["a", "b", "c"]));
console.log(getScore(["a", "b", "c"], ["a", "a", "c"]));
console.log(getScore(["a", "b", "c"], ["a", "a", "a"]));

Create Dynamically Nested Objects with loops

I wanna create a nested object dynamically. I can create it hard coded. Is it possible to do this with a loop ?
result = {}
keys = ["a", "b", "c", "d"]
result[keys[0]] = {}
result[keys[0]][keys[1]] = {}
result[keys[0]][keys[1]][keys[2]] = {}
result[keys[0]][keys[1]][keys[2]][keys[3]] = "cool"
I want to pass an integer for example if it is "3", this should created an object like:
result = {
"a": {
"b": {
"c": "cool"
}
}
}
If it is 4, :
result = {
"a": {
"b": {
"c": {
"d": "cool"
}
}
}
}
So on ...
edit:
I am also checking result object, in order to create this nested structure. If there is not any field yet, I simply create the object.
Using this structure to group data.
Any chance to check these dynamically ?
if (!result[keys[0]])
if (!result[keys[0]][keys[1]])
if (!result[keys[0]][keys[1]][keys[2]])
You can use reduceRight() for this. It just starts from the inside at the last item in the keys list and works its way out starting with "cool":
let keys = ["a", "b", "c", "d"]
let limit = 3
let result = keys.reduceRight((obj, key) => ({[key]: obj}), "cool")
console.log(result)
To limit where the object stops you can iterate over a slice of the keys. For example:
let keys = ["a", "b", "c", "d"]
let start = 0
let stop = 3 // slices are don't inlcude the last item, so this will stop at index 2
let result = keys.slice(start, stop).reduceRight((obj, key) => ({
[key]: obj
}), "cool")
console.log(result)
simple for-loop solution.
let result = {}
let keys = ["a", "b", "c", "d"]
let depth=3;
let current = result
for(let i=0;i<depth;++i){
let key = keys[i]
if(i == depth-1) current[key] = 'cool'
else current = current[key] = {}
}
console.log(result)
If you like to add to a given object a new property, you could reduce the keys with the object and take default objects for not given keys. At the end assign the value.
function setValue(object, path, value, limit) {
var keys = path.slice(0, limit),
last = keys.pop();
keys.reduce((o, k) => o[k] = o[k] || {}, object)[last] = value;
return object;
}
var result = { foo: 42 },
keys = ["a", "b", "c", "d"];
setValue(result, keys, 'cool');
console.log(result);
setValue(result, keys, 'cool', 3);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
short and simple using reduce
let object1= {}
keys = ['a', 'b', 'c']
keys.reduce((prev,curr,i)=>{
prev[curr] = {}
return prev[curr]
}, object1)
log(object1)
// {
// a:{
// b:{
// c:{}
// }
// }
// }
I'd use a reducer, along with some basic tests to help me out:
https://youtu.be/D6zLI8zrfVs
https://gist.github.com/brianswisher/2ce1ffe3ec08634f78aacd1b7baa31f9

How to add an element to an array only if a condition is fulfilled

There is an arrow function creating an array like:
const myFunction = () => ["a", "b", "c"];
I want to add an argument to it that must add another element if the argument is true.
Like here:
const myFunction = (arg) => ["a", "b", "c", arg ? "d" : null];
the problem with this solution is that is still adds a null element if arg !== true but I want to don't add anything in this case.
You can use array spread. According to the value of arg an empty array or an array that contains d will be spreaded into the result array:
const myFunction = (arg) => ["a", "b", "c", ...arg ? ['d'] : []];
console.log(JSON.stringify(myFunction(true))); // ["a","b","c","d"]
console.log(JSON.stringify(myFunction())); // ["a","b","c"]
You could use concat:
const myFunction = (arg) => ["a", "b", "c"].concat(arg ? ["d"] : []);
console.log(myFunction(true));
console.log(myFunction(false));
You can use Array push().
const myFunction = (arg) => {
const arr = ["a", "b", "c"];
if (arg) arr.push("d");
return arr;
};
console.log(myFunction(true));
console.log(myFunction(false));
You can make the function a bit longer,
create a temporary array,
append the element to the temporary array if required,
and return the temporary array when done
const myFunction = (arg) => {
var tempArray = ["a", "b", "c"];
if (arg) {
tempArray.push("d");
}
return tempArray;
};
console.log(myFunction(true) + "");
console.log(myFunction(false) + "");
const myFunction = (arg) => {
ret = ['a', 'b', 'c']
return arg === true ? ret.concat('d') : ret;
}
In other solutions you have arg ? instead of arg === true ?.
If you want myFunction to return array with 'd' ONLY for arg = true,then you shoulu use mine solution. If you want it to return 'd' for, for example, arg = 17, but not to return it for arg = 0, then use others solution.
You can do this also :
const myMethod = (arg) => {
var tempArray = ["item 1", "item 2", "item 3"];
!arg || tempArray.push("item 4");
return tempArray;
};
console.log(myMethod(false));
console.log(myMethod(true));
Ori has the right answer. Use that for all the modern browsers. If for some reason you're still stuck on an older browser -
["a", "b", "c"].concat(arg ? 'd' : [])
If you store your array in a variable you can make it like this:
const arr = ["a", "b", "c"];
const myFunction = arg => arg === true ? [...arr, "d"] : arr;
console.log(myFunction(true));
console.log(myFunction());

How to merge two arrays in JavaScript and keep their order

I had an whiteboard task that stumped me in the interview, however I have written a solution and wondered if anyone has improvements on it as I'm iterating which the interviewer said not to. The two arrays must be merged with the order being array1[0], array2[0], array1[1], array2[1]... (see expectedResult) etc
const options = [[1, 12, 5], ["a", "b", "c", "d", "e"]]
const expectedResult = [1, "a", 12, "b", 5, "c", "d", "e"]
function mergeArrays(first, second) {
let returnArray = []
first.forEach((value, key) => {
returnArray.push(value)
if (second[key]) returnArray.push(second[key])
if (!first[key + 1] && second[key + 1]) {
returnArray.push(
...second.slice(key + 1, second.length)
)
}
})
return returnArray
}
const result = mergeArrays(options[0], options[1])
console.log(result.toString() === expectedResult.toString(), result)
With reduce (as an alternative to the classical for/while loop control structures)
const options = [[1, 12, 5], ["a", "b", "c", "d", "e"]];
const expectedResult = [1, "a", 12, "b", 5, "c", "d", "e"]
// a is the accumulator
// cV, cI are resp. current value and current index
result = options[0].reduce(function (a, cV, cI) {
return a.concat([cV,options[1][cI]]);
},[]);
result = result.concat(options[1].splice(options[0].length));
console.log(result.toString() === expectedResult.toString(), result)
At each step two elements are added to the accumulator array a using concat.
I go the classic way, with a while loop, because it minimize the checks inside of the loop and appends without another check just the rest of one of the arrays.
function mergeArrays(first, second) {
var min = Math.min(first.length, second.length),
i = 0,
result = [];
while (i < min) {
result.push(first[i], second[i]);
++i;
}
return result.concat(first.slice(min), second.slice(min));
}
const options = [[1, 12, 5], ["a", "b", "c", "d", "e"]];
console.log(mergeArrays(...options));
.as-console-wrapper { max-height: 100% !important; top: 0; }
Instead of using value in if conditions , check for length of array.
Problems I see in code are at conditions
if (second[key]) returnArray.push(second[key])
// will not run if second[key] is 0,null,undefined.
if (!first[key + 1] && second[key + 1])
// will produce unwanted result if value reference is 0,null,undefined.
so instead, check for length would produce better result
So the condition
if (second[key]) returnArray.push(second[key])
can be changed into
if( second.length > key) returnArray.push(second[key])
You can use a recursive zipping function, using spread to feed an array of two into it as its parameters:
var z = (a, b) => a.length ? [a[0], ...z(b, a.slice(1))] : b;
var options =
[
[1, 12, 5],
["a", "b", "c", "d", "e"]
];
var expectedResult = z(...options);
console.log(JSON.stringify(expectedResult));
or for any number of array inputs:
var z = (a = [], ...b) =>
b.length ? a.length ? [a[0], ...z(...b, a.slice(1))] : z(...b) : a;
var options =
[
[1, 2],
'♦♡♣♤♥♢',
['A', 'B', 'C'],
['😊', '😔', '😠'],
[null, NaN, undefined]
];
var expectedResult = z(...options);
var stringify = (o) => JSON.stringify(o, (k, v) => v === undefined ? '__undefined__' : v !== v ? '__NaN__' : v).replace(/"__undefined__"/g, 'undefined').replace(/"__NaN__"/g, 'NaN');
console.log(stringify(expectedResult));

Javascript: Sort multi dimensional Array based on first arrays sort results

I have an array setup similar to this:
var ary1 = new Array("d", "a", "b", "c");
var ary2 = new Array("ee", "rr", "yy", "mm");
var mdAry = new Array(ary1, ary2);
ary1 and ary2 indexes are info related to each other in the grand scheme of things.
d ee
a rr
b yy
c mm
I can sort() ary1 and get:
a
b
c
d
but if I sorted ary2 independently I would get:
ee
mm
rr
yy
which visually breaks ary1 and ary2 connections when listed out. Can I retrieve ary1's sorted solution and apply that to ary2? I want to get this:
a rr
b yy
c mm
d ee
If not, could mdAry be sorted so that it applies mdAry[0] sorted solution to the remaining indicies?
If your array items are related, then store them together:
var arr = [
{x: 'd', y: 'ee'},
{x: 'a', y: 'rr'},
{x: 'b', y: 'yy'},
{x: 'c', y: 'mm'}
];
arr.sort(function(a, b) {
if (a.x != b.x) {
return a.x < b.x ? -1 : 1;
}
return 0;
});
One way to do this is to transform the data structure to something that can be sorted more easily, and then transform it back after
var ary1 = ["d", "a", "b", "c"],
ary2 = ["ee", "rr", "mm", "yy"]
mdAry = [ary1, ary2];
// convert to form [[d, ee], [a, rr], ..]
var tmp = mdAry[0].map(function (e, i) {
return [e, mdAry[1][i]];
});
// sort this
tmp.sort(function (a, b) {return a[0] > b[0];});
// revert to [[a, b, ..], [rr, mm, ..]]
tmp.forEach(function (e, i) {
mdAry[0][i] = e[0];
mdAry[1][i] = e[1];
});
// output
mdAry;
// [["a", "b", "c", "d"], ["rr", "mm", "yy", "ee"]]
Just to add yet another method in there, you could get a sort "result" from the first array and apply that to any other related list:
function getSorter(model) {
var clone = model.slice(0).sort();
var sortResult = model.map(function(item) { return clone.indexOf(item); });
return function(anyOtherArray) {
result = [];
sortResult.forEach(function(idx, i) {
result[idx] = anyOtherArray[i];
});
return result;
}
}
Then,
var arr = ["d", "a", "b", "c"];
var arr2 = ["ee", "rr", "yy", "mm"];
var preparedSorter = getSorter(arr);
preparedSorter(arr2);
//=> ["rr", "yy", "mm", "ee"];
Or,
multidimensional = [arr, arr2];
multidimensional.map(getSorter(arr));
// => [["a", "b", "c", "d"], ["rr", "yy", "mm", "ee"]]
You could "merge" them into a single object with two properties, sort by the first one, and then separate back in the end (see demo here):
function sortBoth(ary1, ary2) {
var merged = [];
for (var i=0; i < ary1.length; i++) merged.push({'ary1': ary1[i], 'ary2': ary2[i]});
merged.sort(function(o1, o2) { return ((o1.ary1 < o2.ary1) ? -1 : ((o1.ary1 == o2.ary1) ? 0 : 1)); });
for (var i=0; i < merged.length; i++) { ary1[i] = merged[i].ary1; ary2[i] = merged[i].ary2; }
}
var ary1 = new Array("d", "a", "b", "c");
var ary2 = new Array("ee", "rr", "mm", "yy");
console.log(ary1);
console.log(ary2);
sortBoth(ary1, ary2);
console.log(ary1);
console.log(ary2);
Output:
[ "d", "a", "b", "c"]
["ee", "rr", "mm", "yy"]
[ "a", "b", "c", "d"]
["rr", "mm", "yy", "ee"]
In your example the result should be (if I understand correctly)
a rr
b mm
c yy
d ee
So this one should to the job:
Array.prototype.sortRelated = function(related) {
var clone = this.slice(0),
sortedRelated = [];
clone.sort();
for(var i = 0; i < this.length; i ++) {
sortedRelated[clone.indexOf(this[i])] = related[i];
}
return sortedRelated;
}
var ary1 = new Array("d", "a", "b", "c");
var ary2 = new Array("ee", "rr", "mm", "yy");
var sorted = ary1.sortRelated(ary2);
Working demo here: http://jsfiddle.net/cwgN8/

Categories

Resources