It is a simple exercise that I am doing for mere practice and leisure, I have done it in various ways but I was wondering if there is an even more practical way or to reduce the lines of code making use of the many methods of JavaScript.
The exercise is about receiving an array (arr) and a number (target) and returning another array with a pair of numbers found in 'arr' whose sum is equal to 'target'.
function targetSum3(arr, target) {
let newArr = [];
let copyArray = arr;
for (let i of copyArray) {
let x = Math.abs(i - target);
copyArray.pop(copyArray[i]);
if (copyArray.includes(x) && (copyArray.indexOf(x) != copyArray.indexOf(i))) {
newArr.push(i);
newArr.push(x);
return newArr;
}
}
return newArr;
}
If you are fine with a function that just returns a pair of numbers (the first match so to speak) whose sum equals the targets value, this might be enough:
function sumPair (arr, target) {
while(arr.length) {
let sum1 = arr.shift();
let sum2 = arr.find(val => sum1 + val === target);
if (sum2) return [sum2, sum1];
}
return null;
}
const targetSum = (arr, target) => {
const first = arr.find((v,i,a) => arr.includes(target-v) && (arr.indexOf(target-v) !== i));
return first ? [first, target - first] : null;
};
const values = [1,2,3,4,5,6,7,8,9];
console.log(targetSum(values, 1)); // null
console.log(targetSum(values, 2)); // null
console.log(targetSum(values, 3)); // [1, 2]
console.log(targetSum(values, 15)); // [6, 9]
console.log(targetSum(values, 20)); // null
I changed for loop with forEach (more efficient) and there is no need for the copyArray array so I removed it. I also changed pop() with shift(), I think you want to shift the array and not pop-it (if I understand the task correctly).
function targetSum3(arr, target) {
let newArr = [];
arr.forEach(element => {
let x = Math.abs(element - target); // calc x
arr.shift(); // removes first element from arr (current element)
if (arr.includes(x) && (arr.indexOf(x) != arr.indexOf(element))) {
newArr.push(element);
newArr.push(x);
return;
}
});
return newArr;
}
use Array.filter to find the target sum for all values in an given array. See comments in the snippet.
sumsForTargetInArray();
document.addEventListener(`click`,
evt => evt.target.id === `redo` && sumsForTargetInArray());
function sumsInArray(arr, target) {
// clone the array
const clone = arr.slice();
let result = [];
while (clone.length) {
// retrieve the current value (shifting it from the clone)
const current = clone.shift();
// filter arr: all values where value + sum = target
const isTarget = arr.filter(v => current + v === target);
// add to result.
// Sorting is to prevent duplicates later
if (isTarget.length) {
result = [...result, ...isTarget.map(v => [current, v].sort())];
}
}
// weed out duplicates (e.g. 0 + 3, 3 + 0)
const unique = new Set();
result.forEach(r => unique.add(`${r[0]},${r[1]}`));
// return array of array(2)
return [...unique].map(v => v.split(`,`).map(Number));
}
function sumsForTargetInArray() {
const testArr = [...Array(20)].map((_, i) => i);
const target = Math.floor(Math.random() * 30);
document.querySelector(`pre`).textContent = `testArray: ${
JSON.stringify(testArr)}\ntarget: ${target}\nResult: ${
JSON.stringify(sumsInArray(testArr, target))}`;
}
<pre></pre>
<button id="redo">Again</button>
I have an array of objects:
const bookDetails = [{"author":"john","readingTime":12123},
{"author":"romero","readingTime":908},
{"author":"romero","readingTime":1212},
{"author":"romero","readingTime":50},
{"author":"buck","readingTime":1902},
{"author":"buck","readingTime":12125},
{"author":"romero","readingTime":500},
{"author":"john","readingTime":10},
{"author":"romero","readingTime":230},
{"author":"romero","readingTime":189},
{"author":"legend","readingTime":12}
{"author":"john","readingTime":1890}]
I tried calculating the median for each author. Here is my function that calculates median for a given array:
//To calculate the median for a given array
function medianof2Arr(arr1) {
var concat = arr1;
concat = concat.sort(function (a, b) { return a - b });
var length = concat.length;
if (length % 2 == 1) {
// If length is odd
return concat[(length / 2) - .5]
} else {
return (concat[length / 2] + concat[(length / 2) - 1]) / 2;
}
}
But I want to calculate the median for each author separately. How can I do that?
Expected output
{"john": 1890, "romero": 365, "buck": 7014, "legend": 12}
Can you please try this
let bookDetails = [
{"author":"john","readingTime":12123},
{"author":"romero","readingTime":908},
{"author":"romero","readingTime":1212},
{"author":"romero","readingTime":50},
{"author":"buck","readingTime":1902},
{"author":"buck","readingTime":12125},
{"author":"romero","readingTime":500},
{"author":"john","readingTime":10},
{"author":"romero","readingTime":230},
{"author":"romero","readingTime":189},
{"author":"legend","readingTime":12},
{"author":"john","readingTime":1890}
];
const authorMap = bookDetails.reduce((acc, book) => {
acc[book.author] ? acc[book.author].push(book.readingTime) : acc[book.author] = [book.readingTime]
return acc;
}, {})
calculateMedian = (list) => {
const sortedList = list.sort((a, b) => a - b);
console.log(sortedList[Math.floor(sortedList.length / 2)]); // You might need to tweak this
}
for (let author in authorMap) {
calculateMedian(authorMap[author])
}
You could first group your input by author, to get this data structure:
{
"john": [12123, 10, 1890],
"romero": [908, 1212, 50, 500, 230, 189],
"buck": [1902, 12125],
"legend": [12]
}
And then you could call the median function on all those arrays and replace those arrays with the values you get back from the calls:
function median(arr) {
arr = [...arr].sort((a, b) => a - b);
let mid = arr.length >> 1;
return arr.length % 2 ? arr[mid] : (arr[mid-1] + arr[mid]) / 2;
}
const bookDetails = [{"author":"john","readingTime":12123}, {"author":"romero","readingTime":908}, {"author":"romero","readingTime":1212}, {"author":"romero","readingTime":50}, {"author":"buck","readingTime":1902}, {"author":"buck","readingTime":12125}, {"author":"romero","readingTime":500},{"author":"john","readingTime":10},{"author":"romero","readingTime":230}, {"author":"romero","readingTime":189}, {"author":"legend","readingTime":12},{"author":"john","readingTime":1890}];
// Create a key for each author, and link them with an empty array
let result = Object.fromEntries(bookDetails.map(({author}) => [author, []]));
// Populate those arrays with the relevant reading times
for (let {author, readingTime} of bookDetails) result[author].push(readingTime);
// Replace those arrays with their medians:
for (let author in result) result[author] = median(result[author]);
console.log(result);
Note that the median for buck is not an integer 7014 as in your expected output, but 7013.5
I have an array of object [{a:1},{a:2},{a:3},{a:4},{a:5}].
I want to find index of {a:3} and remove all next elements from index of {a:3}.
Result should look like [{a:1},{a:2},{a:3}] and removed elements will be {a:4},{a:5}.
You can use Array#findIndex() to find that object by its number, or Array#indexOf() if you have a reference to it.
Then, you can either set the array's length (mutating) or use Array#slice() (not mutating), to reduce its length:
const array = [{a:1},{a:2},{a:3},{a:4},{a:5}]
const i = array.findIndex(({a}) => a === 3)
const arrayCopy = array.slice(0, i + 1)
//or:
array.length = i + 1
console.log(i)
console.log(arrayCopy)
console.log(array)
Or with Array#indexOf():
//If you have a reference from somewhere:
const ref = {a:3}
const array = [{a:1},{a:2},ref,{a:4},{a:5}]
const i = array.indexOf(ref)
const arrayCopy = array.slice(0, i + 1)
//or:
array.length = i + 1
console.log(i)
console.log(arrayCopy)
console.log(array)
Alternatively, if you don't have a reference, but it's guaranteed that the array is sorted, you can use a binary search, that makes this much faster for large arrays:
function binarySearch(array, target, cb) {
let start = 0, end = array.length
while(start < end){
const middle = ((end - start - 1) >>> 1) + start
const middleVal = cb(array[middle], middle)
if(middleVal === target) return middle
if(middleVal < target)
start = middle + 1
else
end = middle
}
return -1
};
const array = [{a:1},{a:2},{a:3},{a:4},{a:5}]
const i = binarySearch(array, 3, ({a}) => a)
const arrayCopy = array.slice(0, i + 1)
//or:
array.length = i + 1
console.log(i)
console.log(arrayCopy)
console.log(array)
One line vanilla JS with slice and findIndex
const data = [{ a: 1 }, { a: 2 }, { a: 3 }, { a: 4 }, { a: 5 }];
const res = data.slice(
0,
data.findIndex((el) => JSON.stringify(el) === JSON.stringify({ a: 3 })) + 1
);
console.log(res);
Problem
I have a string of numerical values separated by commas, and I want to include them in an array, and also each pair of them to be an array nested inside of the main array to be my drawing vertices.
How do I solve this problem?
Input:
var vertices = "24,13,47,20,33,9,68,18,99,14,150,33,33,33,34,15,91,10";
what I want them to be is:
Output:
var V_array = [[24,13],[47,20],[33,9],[68,18],[99,14],[150,33],[33,33],[34,15],[91,10]];
You could Split on every second comma in javascript and map the splitted pairs by converting the values to number.
var vertices = "24,13,47,20,33,9,68,18,99,14,150,33,33,33,34,15,91,10",
result = vertices.match(/[^,]+,[^,]+/g).map(s => s.split(',').map(Number));
console.log(result);
You can use the function reduce which operates over the splitted-string and check for the mod of each index.
let str = "24,13,47,20,33,9,68,18,99,14,150,33,33,33,34,15,91,10";
let result = str.split(',').reduce((a, s, i) => {
a.curr.push(Number(s));
if ((i + 1) % 2 === 0) {
a.arr.push(a.curr);
a.curr = [];
}
return a;
}, {arr: [], curr: []}).arr;
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can split string into array and use reduce method. Take a look at the code below
const vertices = "24,13,47,20,33,9,68,18,99,14,150,33,33,33,34,15,91,10";
const numbers = vertices.split(',').map(Number)
const res = numbers
.reduce((acc, number, index, srcArray) => {
if (index % 2) {
return acc
}
return [
...acc,
[ number, srcArray[index + 1] ],
]
}, [])
console.log(res)
My two cents :) [new version]
let
str = "24,13,47,20,33,9,68,18,99,14,150,33,33,33,34,15,91,10",
pair = [],
triplet = [];
JSON.parse(`[${str}]`).forEach((e,i)=>{pair.push( (i%2)?[pair.pop(),e]:e)})
console.log ( 'pair:', JSON.stringify(pair) )
// bonus => same idea for triplet :
JSON.parse(`[${str}]`).forEach((e,i)=>{
if ( (i%3)===2 ) triplet.push( [triplet.shift(),triplet.pop(),e] )
else if ( (i%3)===0 ) triplet.unshift(e)
else triplet.push(e)
})
console.log ( 'triplet:', JSON.stringify(triplet) )
You can use exec and JSON.parse
var vertices = "24,13,47,20,33,9,68,18,99,14,150,33,33,33,34,15,91,10";
var array1;
var reg = /[^,]+,[^,]+/g
let op = []
while((array1 = reg.exec(vertices))!== null){
op.push(JSON.parse(`[${array1[0]}]`))
}
console.log(op)
Split on the , and use Array.reduce to group the pair into a new 2-D array:
var vertices = "24,13,47,20,33,9,68,18,99,14,150,33,33,33,34,15,91,10";
const pair = vertices.split(",").reduce((acc, ele, idx, arr) => {
if(idx === 0 || idx%2 === 0) {acc.push([+ele, +arr[idx + 1]]);}
return acc;
}, []);
console.log(pair);
Same can be done using Array.map, if the index is odd skip the element and filter out the undefined elements:
var vertices = "24,13,47,20,33,9,68,18,99,14,150,33,33,33,34,15,91,10";
const pair = vertices.split(",").map((ele, idx, arr) => (idx === 0 || idx%2 === 0) ? [+ele, +arr[idx + 1]] : undefined).filter(e => e);
console.log(pair);
My two cents :)
( thanks to Code Maniac for the idea of using JSON.parse )
let str = "24,13,47,20,33,9,68,18,99,14,150,33,33,33,34,15,91,10";
let result = JSON.parse(`[${str}]`).reduce((acc, cur, i) => {
if (i===1) return [[acc,cur]]
if (i%2) acc.push( [acc.pop(), cur] )
else acc.push( cur )
return acc
});
console.log ( result )
Here is my solution.
var vertices = "24,13,47,20,33,9,68,18,99,14,150,33,33,33,34,15,91,10";
vertices = vertices.split(",");
function convertToMultiArray (arr, length) {
var nArr = [];
while(arr.length > 0) {
nArr.push(arr.splice(0,length));
}
return nArr;
}
const res = convertToMultiArray(vertices, 2);
console.log('res', res);
I have an array of numbers from 1 to 60
var originalArray = [1, 2, 3, 4 .... 58, 59, 60] // etc
I want to - depending on another number between 2 and 4 - split those numbers randomly into the number of arrays specified, and for the result to be unique each and every time.
For example:
distributeArray(2) should result in two arrays, each with 30 numbers randomly selected from the original array.
distributeArray(3) should result in three arrays, each with 20 numbers randomly selected from original array.
I assume this is a reasonably common case so any pointers would be appreciated. Thanks in advance.
You could do something like this, first shuffle and then split array into n parts.
var arr = [...Array(61).keys()].slice(1)
function splitRandom(data, n) {
var seen = [];
var counter = 0;
var shuffle = data.reduce(function(r, e) {
function random() {
var rand = parseInt(Math.random(0, arr.length) * arr.length);
if (seen.indexOf(rand) != -1) {
return random()
} else {
seen.push(rand)
return rand;
}
}
r[random()] = e;
return r;
}, [])
var split = shuffle.reduce(function(r, e) {
var c = counter++;
r[c] = r[c].concat(e)
counter = counter % n;
return r;
}, Array(n).fill([]))
return split;
}
console.log(JSON.stringify(splitRandom(arr, 3)))
console.log(JSON.stringify(splitRandom(arr, 10)))
console.log(JSON.stringify(splitRandom(arr, 50)))
You can create a function which creates an array of n .length, and an array of x .length. Use do..while loop Array.prototype.splice() to remove a random index from originalArray, .push() the element to one of x random arrays, until originalArray.length evaluates to false, return array of arrays containing values.
const randomArrays = (n, x) => {
let [originalArray, result, len] = [
Array.from({length: n}, (_, key) => key)
, Array.from({length: x}, () => [])
, Math.ceil(n / x)
];
do {
let [curr, index] = [
originalArray
.splice(Math.floor(Math.random() * originalArray.length), 1)
.pop()
, Math.floor(Math.random() * result.length)
];
if (result[index].length < len)
result[index].push(curr);
else
for (let i = 0; i < result.length; i++) {
if (result[i].length < len) {
result[i].push(curr);
break;
}
}
} while (originalArray.length);
return result
}
console.log(
randomArrays(60, 3)
, randomArrays(21, 7)
, randomArrays(5, 3)
, randomArrays(27, 5)
);