Code review, help improving this code to avoid duplication - javascript

I working on the Sudoku puzzle, the following code works but it's easy to see that there is a lot of duplicated code. How can I optimize it? Thanks
Problem: getSection: This function should accept three arguments: a sudoku grid, and an x and y coordinate for one of the puzzle's 3x3 subgrids. The function should return an array with all the numbers in the specified subgrid.
Input example:
var puzzle = [[ 8,9,5, 7,4,2, 1,3,6 ],
[ 2,7,1, 9,6,3, 4,8,5 ],
[ 4,6,3, 5,8,1, 7,9,2 ],
[ 9,3,4, 6,1,7, 2,5,8 ],
[ 5,1,7, 2,3,8, 9,6,4 ],
[ 6,8,2, 4,5,9, 3,7,1 ],
[ 1,5,9, 8,7,4, 6,2,3 ],
[ 7,4,6, 3,2,5, 8,1,9 ],
[ 3,2,8, 1,9,6, 5,4,7 ]];
Output:
getSection(puzzle, 0, 0);
// -> [ 8,9,5,2,7,1,4,6,3 ]
Solution:
function getSection(arr, x, y) {
var section = [];
if (y === 0) {
arr = arr.slice(0, 3);
if (x === 0) {
arr.forEach(function (element) {
section.push(element.slice(0, 3));
})
} else if (x === 1) {
arr.forEach(function (element) {
section.push(element.slice(3, 6));
})
} else {
arr.forEach(function (element) {
section.push(element.slice(6, 9));
})
}
}
if (y === 1) {
arr = arr.slice(4, 7);
if (x === 0) {
arr.forEach(function (element) {
section.push(element.slice(0, 3));
})
} else if (x === 1) {
arr.forEach(function (element) {
section.push(element.slice(3, 6));
})
} else {
arr.forEach(function (element) {
section.push(element.slice(6, 9));
})
}
}
if (y === 2) {
arr = arr.slice(6, 9);
if (x === 0) {
arr.forEach(function (element) {
section.push(element.slice(0, 3));
})
} else if (x === 1) {
arr.forEach(function (element) {
section.push(element.slice(3, 6));
})
} else {
arr.forEach(function (element) {
section.push(elemet.slice(6, 9));
})
}
}
var subgrid = section.reduce(function (a, b) {
return a.concat(b);
},
[]
);
return subgrid;
}
console.log(getSection(puzzle, 0, 0));
// // -> [ 8,9,5,2,7,1,4,6,3 ]
console.log(getSection(puzzle, 1, 0));
// -> [ 7,4,2,9,6,3,5,8,1 ]

Here's my take using ES6
const puzzle = [
[8, 9, 5, 7, 4, 2, 1, 3, 6],
[2, 7, 1, 9, 6, 3, 4, 8, 5],
[4, 6, 3, 5, 8, 1, 7, 9, 2],
[9, 3, 4, 6, 1, 7, 2, 5, 8],
[5, 1, 7, 2, 3, 8, 9, 6, 4],
[6, 8, 2, 4, 5, 9, 3, 7, 1],
[1, 5, 9, 8, 7, 4, 6, 2, 3],
[7, 4, 6, 3, 2, 5, 8, 1, 9],
[3, 2, 8, 1, 9, 6, 5, 4, 7]
];
const GRID_SIZE = 3;
function getOffset(coordinate) {
const start = coordinate * GRID_SIZE;
const end = start + GRID_SIZE;
return [start, end];
}
function getSection(arr, x, y) {
const yOffset = getOffset(y);
const xOffset = getOffset(x);
const elements = arr.slice(...yOffset);
return elements
.map(element => element.slice(...xOffset))
.reduce((subgrid, grid) => [...subgrid, ...grid], []);
}
console.log(getSection(puzzle, 0, 0));
// // -> [ 8,9,5,2,7,1,4,6,3 ]
console.log(getSection(puzzle, 1, 0));
// -> [ 7,4,2,9,6,3,5,8,1 ]

I assume your x and y will not exceed your array length. Here's the simplest way to achieve the solution.
function getSection(arr, x, y) {
var GRID_SIZE = 3;
var indexX = x*GRID_SIZE;
var indexY = y*GRID_SIZE;
var results = [];
for(var i = indexY; i< indexY+GRID_SIZE; i++){
results = results.concat(puzzle[i].slice(indexX, indexX+GRID_SIZE));
}
return results;
}
console.log(getSection(puzzle, 0, 0));
// // -> [ 8,9,5,2,7,1,4,6,3 ]
console.log(getSection(puzzle, 1, 0));
// -> [ 7,4,2,9,6,3,5,8,1 ]

Not as elegant as #nutboltu but almost as concise.
function getSection(arr, x, y) {
var section = [];
z = (y===0?0:y+y+2);
arr = arr.slice(z, z+3);
arr.forEach(function (element) {
section.push(element.slice(z, z+3));
})
var subgrid = section.reduce(function (a, b) {
return a.concat(b);
},
[]
);
return subgrid;
}

Related

How to return all even values to the beginning of the array

I need to organize the arrays that are inside an array so that the first numbers in the array are even
For now I can only scroll through the arrays, I couldn't implement the logic
let matriz = [
[5, 1, 2, 4, 7, 9],
[2, 4, 3, 1, 7, 9],
[1, 2, 3, 4, 5, 6],
]
for (let i = 0; i < matriz.length; i++) {
let innerArrLength = matriz[i].length
for (let j = 0; j < innerArrLength; j++) {
if (matriz[i][j] % 2 === 0) {
// code
} else {
// code
}
}
}
`
You could sort the array by taking a function which returns true fro even values.
const
move = (array, fn) => array.sort((a, b) => fn(b) - fn(a)),
array = [5, 1, 2, 4, 7, 9];
move(array, v => v % 2 === 0);
console.log(...array);
If you don't mind a less performant solution, filter can be very clear here:
let matriz = [
[5, 1, 2, 4, 7, 9],
[2, 4, 3, 1, 7, 9],
[1, 2, 3, 4, 5, 6],
]
for (let i = 0; i < matriz.length; i++) {
matriz[i] = matriz[i].filter(x => x%2 == 0)
.concat(
matriz[i].filter(x => x%2 != 0))
}
console.log(matriz)
(the problem is that you traverse twice every inner array and you could do it with a single pass)

Problem with using for-in/of when returning an object in a function

I just got the result "[object Object]'s score is 0" printed on the terminal.
The result 27 was all fine until I separated the function into a return object.
How do I get 27 if I have to return an object?
How do I get "alex" printed on the console.log instead of [object Object]?
const alex = {
first: [1, 2, 9, 8],
second: [3],
third: [0, 0, 0, 1, 3]
};
const gordon = {
first: [3],
second: [2, 2, 4, 5, 6, 6, 7, 8]
}
function createPlayer(object) {
let score = 0;
return {
add: function() {
for (const key in object) {
for (const item in object[key]) {
score += object[key][item]
}
}
},
result: function() {
return `${object}\'s score is ${score}`
}
}
}
createPlayer(alex).add()
console.log(createPlayer(alex).result())
const alex = {
first: [1, 2, 9, 8],
second: [3],
third: [0, 0, 0, 1, 3]
};
const gordon = {
first: [3],
second: [2, 2, 4, 5, 6, 6, 7, 8]
}
function createPlayer(object, name) {
let score = 0;
return {
add: function() {
for (const key in object) {
for (const item in object[key]) {
score += object[key][item]
}
}
return this; // <<<<<
},
result: function() {
return `${name}\'s score is ${score}`
}
}
}
console.log(createPlayer(alex, 'Alex').add().result())
You would not show alex for an object named alex
You might mean this
const alex = {
Name: "Alex",
first: [1, 2, 9, 8],
second: [3],
third: [0, 0, 0, 1, 3]
};
const gordon = {
Name: "Gordon",
first: [3],
second: [2, 2, 4, 5, 6, 6, 7, 8]
}
function createPlayer(object) {
let score = 0;
return {
add: function() {
for (const key in object) {
if (key!=="Name") {
for (const item in object[key]) {
score += object[key][item]
}
}
}
},
result: function() {
return `${object.Name}\'s score is ${score}`
}
}
}
const player1 = createPlayer(alex)
player1.add()
console.log(player1.result())

Why can't I use an array as the 'specified condition' in a while loop?

So I have this code:
var myArray = [];
var value = 5;
while (myArray != [5, 4, 3, 2, 1, 0]) {
myArray.push(value)
value--;
console.log(myArray);
}
when I look at the console, it goes on for an infinite loop like so..
[ 5 ]
[ 5, 4 ]
[ 5, 4, 3 ]
[ 5, 4, 3, 2 ]
[ 5, 4, 3, 2, 1 ]
[ 5, 4, 3, 2, 1, 0 ]
[ 5, 4, 3, 2, 1, 0, -1 ]
[ 5, 4, 3, 2, 1, 0, -1, -2 ]
[ 5, 4, 3, 2, 1, 0, -1, -2, -3 ]
..........
Why doesn't it stop at [5,4,3,2,1,0] ? myArray = that at a point and the for loop should stop no?
Sorry for the noob question.
JavaScript does not provide built-in support for structural-equality of Arrays, but it's straightforward to implement a comparator:
function arraysEqual(a, b, orderSensitive = true) {
// Function from https://stackoverflow.com/a/16436975/159145
// But modified to add the `orderSensitive` option.
if (a === b) return true;
if (a == null || b == null) return false;
if (a.length != b.length) return false;
if (!orderSensitive) {
a = Array.from(a).sort();
b = Array.from(b).sort();
}
for (var i = 0; i < a.length; ++i) {
if (a[i] !== b[i]) return false;
}
return true;
}
function yourCode() {
var myArray = [];
var value = 5;
const finalArray = [5, 4, 3, 2, 1, 0];
while (!arraysEqual(myArray,finalArray)) {
myArray.push(value)
value--;
console.log(myArray);
}
}
you can use the following way to fix the issue if index and values of both arrays are different
while(sortArray(myArray).toString() !== sortArray([5, 4, 3, 2, 1, 0]).toString()) {
// your code
}
sortArray(array) {
return array.filter((a, b) => a-b)
}
If you are sure the array index and values for both arrays are same you can use
while(myArray.toString() !== [5, 4, 3, 2, 1, 0].toString()) {
// your code
}
#Christopher Barreto and welcome to StackOverflow. Good luck with your interesting question.
although #Dai right with his full answer there is much simpler built in a way to convert array to string and then compare them.
That will work for you:
var myArray = [];
var value = 5;
while (myArray.toString() != [5, 4, 3, 2, 1, 0].toString()) {
myArray.push(value)
value--;
console.log(myArray);
}
Or this if you prefer:
while ((''+myArray) != ('' + [5, 4, 3, 2, 1, 0]))

Sort array of numbers by normal distribution (gaussian distribution)

Having an array of numbers setOfNumbers = [0, 3, 3, 2, 7, 1, -2, 9] I'd like to sort this set to have the smallest numbers on the end and beginning and the biggest in the center of the sorted set like this sortedSetNumbers = [0, 2, 3, 9, 7, 3, 1, -2].
const setOfNumbers = [0, 3, 3, 2, 7, 1, -2, 9];
const result = [0, 2, 3, 9, 7, 3, 1, -2];
function sortNormal(a, b) {
return true; // Please, change this line
}
const sortedSetNumbers = setOfNumbers.sort((a, b) => sortNormal(a, b));
if (sortedSetNumbers === result) {
console.info('Succeeded Normal Distributed');
} else {
console.warn('Failed Normal Distribution');
}
console.log(sortedSetNumbers);
I am sure it is possible to sort these numbers with the method Array.prototype.sort(), but how should this sorting function look like?
EDIT: The solution does not have to be solved with .sort(). That was only an idea.
This might be the most naive way to do it, but isn't it simply left, right, left, right... after sorting?
const input = [0, 3, 3, 2, 7, 1, -2, 9];
const expected = [0, 2, 3, 9, 7, 3, 1, -2];
const sorted = input.slice().sort();
const output = [];
let side = true;
while (sorted.length) {
output[side ? 'unshift' : 'push'](sorted.pop());
side = !side;
}
console.log(expected.join());
console.log(output.join());
Or simply:
const input = [0, 3, 3, 2, 7, 1, -2, 9];
const output = input.slice().sort().reduceRight((acc, val, i) => {
return i % 2 === 0 ? [...acc, val] : [val, ...acc];
}, []);
console.log(output.join());
A slightly different approach is to sort the array ascending.
Get another array of the indices and sort the odds into the first half asending and the even values to the end descending with a inverted butterfly shuffle.
Then map the sorted array by taking the value of the sorted indices.
[-2, 0, 1, 2, 3, 3, 7, 9] // sorted array
[ 1, 3, 5, 7, 6, 4, 2, 0] // sorted indices
[ 0, 2, 3, 9, 7, 3, 1, -2] // rebuild sorted array
var array = [0, 3, 3, 2, 7, 1, -2, 9].sort((a, b) => a - b);
array = Array
.from(array, (_, i) => i)
.sort((a, b) => b % 2 - a % 2 || (a % 2 ? a - b : b - a))
.map(i => array[i]);
console.log(array);
This solution is not really elegant, but it does it's job.
const setOfNumbers = [0, 3, 3, 2, 7, 1, -2, 9];
const alternation = alternate();
const sortedSetNumbers = sortNormal(setOfNumbers);
function sortNormal(start) {
const result = [];
const interim = start.sort((a, b) => {
return b - a;
});
interim.map(n => {
if (alternation.next().value) {
result.splice(0, 0, n);
} else {
result.splice(result.length, 0, n);
}
});
return result;
}
function* alternate() {
let i = true;
while (true) {
yield i;
i = !i;
}
}
console.log(sortedSetNumbers);

javascript several corresponding array reducing/sumup

What is the cleanest way to reduce those array ?
data = {
id: [1, 1, 1, 3, 3, 4, 5, 5, 5, ...]
v: [10,10,10, 5, 10 ...]
}
For each id there is a v corresponding. What I want is sum up v for each id. In this example the result should be
data = {
id: [1, 3, 4, 5, ...]
v: [30, 15, ...]
}
I would go for the Array.prototype.reduce() ,simple and elegant solution
var ids = [1, 1, 1, 3, 3, 3, 3, 4, 5, 6, 6, 6],
v = [10, 10, 10, 5, 10, 10, 10, 404, 505, 600, 60, 6],
data = {};
data.v = [];
data.ids = ids.reduce(function(a, b, index) {
if (a.indexOf(b) < 0) a.push(b);
if (!data.v[a.indexOf(b)]) data.v[a.indexOf(b)] = 0;
data.v[a.indexOf(b)] += v[index];
return a;
}, []);
https://jsfiddle.net/2ssbngLr/
One way of doing this, given two arrays of equal length would be to map/reduce them:
const ids = [1, 1, 1, 3, 3];
const vs = [10,10,10,5,10];
const reduced = ids
.map((val, i) => ({ id: val, value: vs[i] }))
.reduce((agg, next) => {
agg[next.id] = (agg[next.id] || 0) + next.value;
return agg;
}, {});
console.log(reduced);
// Object {1: 30, 3: 15}
Working example: https://jsfiddle.net/h1o5rker/1/
I think it can be accomplished with reduce
var data = {
id: [1, 1, 1, 3, 3],
v: [10, 10, 10, 5, 10]
}
var sumsObjs = data.v.reduce(function(sum, val, index) {
var id = data.id[index];
if (sum[id] !== undefined) {
sum[id] = sum[id] + val;
} else {
sum[id] = val;
}
return sum;
}, {});
console.log(sumsObjs);
<script src="https://getfirebug.com/firebug-lite-debug.js"></script>
var data={
id: [1,1,1,10,123,4531],
v:[123,123,53,223,11,11,11]
},
_v = data.v, vinit;
document.write(data.v+'<br>');
for(var i=0;i<_v.length;i++){
vinit = _v[i];
for(var j=i+1; j<=_v.length;j++){
if(_v[j]===vinit){
delete _v[j];
}
}
};
document.write(data.v);
var data={
id: [1,1,1,10,123,4531],
v:[123,123,53,223,11,11,11,...]
},
_v = data.v, vinit;
for(var i=0;i<_v.length;i++){
vinit = _v[i];
for(var j=i+1; j<=_v.length;j++){
if(_v[j]===vinit){
delete _v[j];
}
}
}
the above code is just for the v but you can simultaneously reduce the repeating elements for id too by introducing some more variables
in the snippet you can see that there are the extra commas in the second line which shows that those elements were deleted
If the ids are always in order, a simple for loop can solve it. There is no need to get overly complicated.
data = {
id: [1, 1, 1, 3, 3, 4, 5, 5, 5],
v: [10, 10, 10, 5, 10, 1, 2, 3, 4]
};
var result = {
id: [],
v: []
};
(function() {
var ids = data.id,
vals = data.v,
lastId = ids[0],
runningTotal = vals[0];
for (var i = 1; i < ids.length; i++) {
if (lastId === ids[i]) {
runningTotal += vals[i];
}
if (lastId !== ids[i] || i + 1 === ids.length) {
result.id.push(lastId);
result.v.push(runningTotal);
lastId = ids[i];
runningTotal = vals[i];
}
}
}());
console.log(result);
Some people have posted some good solutions so far, but I haven't really seen one that does exactly what you're looking for. Here is one that takes your specific object and returns an object of the same format, but meeting your requirements and reduced.
// Your data object
data = {
id: [1, 1, 1, 3, 3],
v: [10,10,10, 5, 10]
}
// Assuming obj consists of `id` and `v`
function reduce(obj){
// We create our reduced object
var reducedObj = {
id: [],
v: []
}
// Next we create a hash map to store keys and values
var map = {};
for(var i=0; i<obj.id.length; ++i){
// If this key doesn't exist, create it and give it a value
if(typeof map[parseInt(obj.id[i])] === 'undefined'){
map[parseInt(obj.id[i])] = 0;
}
// Sum all of the values together for each key
map[parseInt(obj.id[i])] += parseInt(obj.v[i]);
}
// Now we map back our hashmap to our reduced object
for(var ele in map){
reducedObj.id.push(ele);
reducedObj.v.push(map[ele]);
}
// Return our new reduced object
return reducedObj;
}
var myReducedObject = reduce(data);
console.log(myReducedObject);
Working Fiddle
This is a solution for ordered id with Array.prototype.reduce().
var data = {
id: [1, 1, 1, 3, 3, 4, 5, 5, 5],
v: [10, 10, 10, 5, 10, 7, 8, 10, 13]
},
result = { id: [], v: [] };
data.id.reduce(function (r, a, i) {
if (r === a) {
result.v[result.v.length - 1] += data.v[i];
} else {
result.id.push(a);
result.v.push(data.v[i]);
}
return a;
}, -1);
document.write('<pre>' + JSON.stringify(result, 0, 4) + '</pre>');
Or a in situ version
var data = {
id: [1, 1, 1, 3, 3, 4, 5, 5, 5],
v: [10, 10, 10, 5, 10, 7, 8, 10, 13]
};
void function (d) {
var i = 1;
while (i < d.id.length) {
if (d.id[i - 1] === d.id[i]) {
d.id.splice(i, 1);
d.v[i - 1] += d.v.splice(i, 1)[0];
continue;
}
i++;
}
}(data);
document.write('<pre>' + JSON.stringify(data, 0, 4) + '</pre>');

Categories

Resources