JavaScript Pop vs slice methods on an array - javascript

I've got a data set that's several hundred elements long. I need to loop through the arrays and objects and determine if the data in them is less than a certain number (in my case, 0). If it is, I need to remove all those data points which are less than zero from the data set.
I've tried .pop and .slice but I'm not implementing them correctly. I was trying to push the bad data into its own array, leaving me with only the good data left.
Here's my JS
for (var i = 0; i < data.length; i++) {
if (data[i].high < 0) {
console.log(data[i].high)
var badData = [];
badData.push(data.pop(data[i].high));
console.log(data[i].high)
}
}

I'd go with .filter():
const result = data.filter(row => row.high > 0);

In case you need the bad results too.
const { good, bad } = data.reduce((acc, row) => {
const identifier = row.high > 0 ? 'good' : 'bad';
acc[identifier].push(row);
return acc;
}, { bad: [], good: [] });

Related

How to simplify javascript program?

I would love to minimize the program.
Maybe putting p1-16 in one line of code, same with count and gefunden?
Since my language skills are minimal I can't find the right information.
It would also be great if there was a way to minimize the if else statements in search hits pdf.
Right now I do the code by hand to add new pdfs, as in search hits pdf1 to pdf2. Any easier way would greatly help me.
function Suche(str){
p1=document.getElementById('pdf1').innerHTML;
p2=document.getElementById('pdf2').innerHTML;
p3=document.getElementById('pdf3').innerHTML;
p4=document.getElementById('pdf4').innerHTML;
p5=document.getElementById('pdf5').innerHTML;
p6=document.getElementById('pdf6').innerHTML;
p7=document.getElementById('pdf7').innerHTML;
p8=document.getElementById('pdf8').innerHTML;
p9=document.getElementById('pdf9').innerHTML;
p10=document.getElementById('pdf10').innerHTML;
p11=document.getElementById('pdf11').innerHTML;
p12=document.getElementById('pdf12').innerHTML;
p13=document.getElementById('pdf13').innerHTML;
p14=document.getElementById('pdf14').innerHTML;
p15=document.getElementById('pdf15').innerHTML;
p16=document.getElementById('pdf16').innerHTML;
p17=document.getElementById('pdf17').innerHTML;
gefunden1=0;
gefunden2=0;
gefunden3=0;
gefunden4=0;
gefunden5=0;
gefunden6=0;
gefunden7=0;
gefunden8=0;
gefunden9=0;
gefunden10=0;
gefunden11=0;
gefunden12=0;
gefunden13=0;
gefunden14=0;
gefunden15=0;
gefunden16=0;
gefunden17=0;
count1=0;
count2=0;
count3=0;
count4=0;
count5=0;
count6=0;
count7=0;
count8=0;
count9=0;
count10=0;
count11=0;
count12=0;
count13=0;
count14=0;
count15=0;
count16=0;
count17=0;
searchstring=str;
//Search Hits PDF1
endsearch=p1.length;
weiter=1;
if(p1.indexOf(str)>-1){
gefunden1=1;
pos1=p1.indexOf(str)+searchstring.length;
count1=count1+1;}
else{weiter=0;}
for(i = 1; i <=10; i++){
if(weiter==1){
if(p1.indexOf(str,pos1)>-1){
pos2=p1.indexOf(str,pos1)+searchstring.length;
if (pos2<=endsearch){
if(count1<10){
count1=count1+1;
pos1=pos2;}
else{
count1="Mehr als 10";
pos1=pos2;}}
else{
weiter=0;}}
else{
weiter=0;}}}
//Search Hits Pdf2
endsearch=p2.length;
weiter=1;
if(p2.indexOf(str)>-1){
gefunden2=1;
pos1=p2.indexOf(str)+searchstring.length;
count2=count2+1;}
else{weiter=0;}
for(i = 1; i <=10; i++){
if(weiter==1){
if(p2.indexOf(str,pos1)>-1){
pos2=p2.indexOf(str,pos1)+searchstring.length;
if (pos2<=endsearch){
if(count1<10){
count2=count2+1;
pos1=pos2;}
else{
count2="Mehr als 10";
pos1=pos2;}}
else{
weiter=0;}}
else{
weiter=0;}}}
and so on....
Why not use an array called p?
const p = []
for (let i=1; i<18; i++) {
p.push(document.getElementById(`pdf${i}`).innerHTML)
}
You can do the same for gefunden and count. The rest of your code, if repetitive, could go in a function and be called in another for loop.
I agree that this should be on code review. But you have a working code and ask how to make it better. So here you go.
replace all those variables that have the format variableN with an array. As soon as you have such a naming format you most of the time either want to use an array or change the name.
And you definitely want to clean up that function that searches for the occurrence of the given string.
Always define variables using const or let. And add comments to the code.
If something reflects a boolean, then use one instead of 0 or 1.
Make use of comments, that will also help others when looking at your code (and it also helps you if you look at your code after a while).
Use variables instead of magic numbers like 10.
and even if your preferred language is not the one used of the programming language, you should stick with the one the programming language use.
So here is a reworked version of your code:
// use an options parameter to make the function more flexible
function search(str , options) {
// destructuring the options into variables
const {maxCount, pdfCount} = options;
const items = [];
for (let i = 1; i <= pdfCount; i++) {
items.push({
p: document.getElementById(`pdf${i}`).innerHTML,
found: false,
count: 0
})
}
items.forEach(item => {
let count = 0;
let currentPosition = 0; // position where to start searching
let foundAtPosition;
// do-while loop to do at least one search
do {
foundAtPosition = item.p.indexOf(str, currentPosition);
// check if we found an occurence
if (foundAtPosition != -1) {
// increase the count
count++;
// set the current position after the found occurence
currentPosition = foundAtPosition + str.length;
}
// if we found more then maxCount we can leave the loop
if (count > maxCount) {
break;
}
// only continue the loop when something was found
// you could move "count > maxCount" to the while condition
// but the main purpose of the while loop is to iterate over the content
} while (foundAtPosition != -1);
// do the composing the information to be set for the item after the for loop,
// that makes many things clearer as it is not part of the searching process
// set found to true or false by checking if count is larger then 0
item.found = count > 0;
if (count > maxCount) {
item.count = `Mehr als ${maxCount}`;
} else {
item.count = count;
}
})
return items;
}
console.dir(search('hey', {maxCount: 10, pdfCount: 3}))
<div id="pdf1">
heyheyaaheyaaahey
</div>
<div id="pdf2">
heyheyaaheyaaahey
heyheyaaheyaaahey
heyheyaaheyaaahey
</div>
<div id="pdf3">
foo
</div>
You could also utelize str.split([separator[, limit]]) as mention here How to count string occurrence in string? and utilize the limit function.
But if you do that you really need to document the item.p.split(str, maxCount+2).length - 1 construct because that's hard to understand otherwise.
// use an options parameter to make the function more flexible
function search(str , options) {
// destructuring the options into variables
const {maxCount, pdfCount} = options;
const items = [];
for (let i = 1; i <= pdfCount; i++) {
items.push({
p: document.getElementById(`pdf${i}`).innerHTML,
found: false,
count: 0
})
}
items.forEach(item => {
// use maxCount + 2 to figure out if we have more then max count subtract 1 from length to get the actual count
const count = item.p.split(str, maxCount+2).length - 1
// set found to true or false by checking if count is larger then 0
item.found = count > 0;
if (count > maxCount) {
item.count = `Mehr als ${maxCount}`;
} else {
item.count = count;
}
})
return items;
}
console.dir(search('hey', {maxCount: 10, pdfCount: 3}))
<div id="pdf1">
heyheyaaheyaaahey
</div>
<div id="pdf2">
heyheyaaheyaaahey
heyheyaaheyaaahey
heyheyaaheyaaahey
</div>
<div id="pdf3">
foo
</div>
You can use arrays.
Arrays, in a simple way, put lots of info into one variable. For example:
var this_is_an_array=[0,1,2,3,4,5]
Arrays count from 0 and onwards, so do take note to start counting from 0
More in detail at w3 schools: https://www.w3schools.com/js/js_arrays.asp

Unable to pass an array as argument of a javascript function

I'm trying to implement the quickSort algorithm in javascript, i have to extract 10,000 numbers from a txt file, pass them into an array, and pass this as an argument of my quickSort function, using the fs module of nodejs. The code is able to read the 10,000 numbers, and to convert them from an array of string to an array of number, but when i try to pass the array into my function, only 3472 numbers are passed, which i don't understand.
const fs = require('fs');
// Reading the data from the file containing the 10,000 numbers
const file = fs.readFileSync('./quickSort.txt', 'utf-8');
//Checking if it has read all the numbers correctly
console.log(file); // Displays the 10,000 numbers as strings in an array
// Convert them from string to integer
const finalFile = file.split('\n').map(e => {
return parseInt(e, 10);
})
// Checking if it has converted each element of the array to an integer
//console.log(finalFile) displays the array, with the 10,000 elements converted to integers
// Initialize a counter for the comparaisons made by the quickSort algorithm
let comparisons = 0;
// Sort them using quick sort
function quick_Sort(origArray) {
if (origArray.length <= 1) {
return origArray;
} else {
// Checking if the array has been correctly passed as an argument
console.log(origArray.length); //Displays 3742 instead of 10,000
var left = [];
var right = [];
var newArray = [];
var pivot = origArray.pop();
var length = origArray.length;
// I have tried comparisons += length - 1; too, but i doesn't work
comparisons += length;
for (var i = 0; i < length; i++) {
if (origArray[i] <= pivot) {
left.push(origArray[i]);
} else {
right.push(origArray[i]);
}
}
for (var i = 0; i < right.length; i++) {
comparisons++;
if (right[i] < pivot) {
return right.splice(i, 0, pivot);
}
}
return newArray.concat(quick_Sort(left), quick_Sort(right));
}
}
// Display the result
const result = quick_Sort(finalFile);
// expected output: 25
console.log(result);
Thank you very much.
Edit: In fact the problem of the size comes from the last for loop of the function, if i delete it, and insert the pivot between like that, it works (thanks to StardustGogeta) :
return newArray.concat(quick_Sort(left), pivot, quick_Sort(right));
This is a logical error. You need to change
return newArray.concat(quick_Sort(left), quick_Sort(right));
to
return newArray.concat(quick_Sort(left), pivot, quick_Sort(right));
With that change, the program works for me. The problem is that you are accidentally getting rid of (via .pop()) approximately 1/3 of your input values (the pivot values) during sorting.
try this:
const finalFile = file.split('\r?\n').map(.....)
Your parsing code works for me except for one issue: parseInt returns NaN for the last new line so you need to remove the last element from the array like this: finalFile.pop();. However this does not explain why you are seeing such a difference in the number of elements. There must be something different either in the code or the file you posted.

Trying to understand how to use Promise in js

I'm using the native driver for mongoDB. In the db I have about 7 collections and I want create a variable that stores the amount of entries in each collection minus the last collection. Afterwards I want to create another variable that stores the entries of the last collection then I want to pass the variables through the res.render() command and show it on the webpage.
The problem I'm having here is that I'm so used to synchronous execution of functions which in this case goes straight out the window.
The code below is the way I'm thinking, if everything is executed in sync.
var count = 0;
db.listCollections().toArray(function(err,collection){
for(i = 1; i < collection.length;i++){
db.collection(collection[i].name).count(function(err,value){
count = count + value;
})
}
var count2 = db.collection(collection[i].name).count(function(err,value){
return value;
})
res.render('index.html',{data1: count, data2: count2})
})
Obviously this doesn't do want I want to do so I tried playing around with promise, but ended up being even more confused.
You could do something like this with Promises:
Get collection names, iterate over them, and return either count, or entries (if it's the last collection). Then sum up individual counts and send everything to the client.
db.listCollections().toArray()
.then(collections => {
let len = collections.length - 1
return Promise.all(collections.map(({name}, i) => {
let curr = db.collection(name)
return i < len ? curr.count() : curr.find().toArray()
}
))
}
)
.then(res => {
let last = res.pop(),
count = res.reduce((p, c) => p + c)
res.render('index.html', {count, last})
})

Merge arrays with similar values keeping the order of the values inside

Here's an interesting task that I faced today and I cannot think of any easy way to achieve the desired result.
Let's suppose we have a database with the following fields (columns): A,B,C,D,E,F,G but we don't know the names nor the count of the fields.
We receive a set of records from this database in the following format: {A:value1, B:value2, ...}.
If a value is not set for the current record the key will be missing too. This means I can receive {A:value} or {C:value1, D:value2} as valid records. The order of the keys will always stay the same. This means {D:value, C:value} is not a valid record.
I'm trying to recover the field names based on the returned records and keep the order of the keys.
For example I can receive records with the following keys:
A,C,D,E,F
D,F,G
A,B,F
From the example above I should be able to restore the original sequence which is A,B,C,D,E,F,G.
The first record gives us A,C,D,E,F.
The second one tells us that G is after F so now we have A,C,D,E,F,G
The third record gives us that B is after A so now we have A,B,C,D,E,F,G
If the order cannot be determined for sure we can use alphabetical order. Example for this is:
A,B
A,C
In the example above we cannot determine if the original order is A,B,C or A,C,B.
Any ideas how to implement this to work in the general case?
I will be implementing this algorithm using JavaScript but PHP, C++ or Java are also welcome.
EDIT: Do not think of the objects as standart JSON objects. In the real environment the structure is much more complex and the language is not pure JavaScript, but a modified version of ECMAScript. If it will be easier to understand - think only of the keys as an array of values ['A','B','C',...] and try to merge them, keeping the order.
EDIT 2: After struggling for some time and reading some ideas I came with the following solution:
Create an object that holds all relations - which column comes after which from each database record.
Create a relation between each a->b, b->c => a->c (inspired by Floyd–Warshall where each distance is considered as 1 if exists).
Create a sorting function (comparator) that will check if two elements can be compared. If not - alphabetical order will be used.
Get only the unique column names and sort them using the comparator function.
You can find the source-code attached below:
var allComparators = {};
var knownObjects = ['A,C,D,E,F','D,F,G','A,B,F'];
var allFields = knownObjects.join(',').split(',');
for (var i in knownObjects) {
var arr = knownObjects[i].split(',');
for (var i = 0; i < arr.length; i++) {
for (var j = i + 1; j < arr.length; j++) {
allComparators[arr[i]+'_'+arr[j]] = 1;
}
}
}
allFields = allFields.filter(function(value, index, self) {
return self.indexOf(value) === index;
});
for (var i in allFields) {
for (var j in allFields) {
for (var k in allFields) {
if (allComparators[allFields[i]+'_'+allFields[j]] && allComparators[allFields[j]+'_'+allFields[k]]) {
allComparators[allFields[i]+'_'+allFields[k]] = 1;
}
}
}
}
allFields.sort(function(a, b) {
if (typeof allComparators[a + '_' + b] != 'undefined') {
return -1;
}
if (typeof allComparators[b + '_' + a] != 'undefined') {
return 1;
}
return a > b;
});
console.log(allFields);
I give you the algorithm in a very direct and understandable way but the code! please try yourself and ask for help if required.
I express myself in two ways
In technical terms :
Generate a precedence graph (that is a directed graph)
Topological sort it
In more details :
Graph : Map(String, ArrayList< String >) = [Map(key,value)]
each key in the map corresponds to an element (A,B,C,...)
each value contains the elements that should place after the key,e.g for A it is {B,C,D,...}
How to fill the graph :
for each row:
for each element inside the row:
if the element is already as a key in the map
just add its immediate next item to the list*
else
add the element to the map and set the value to immediate next element of it**
* if the element is the last one in the row don't add anything to the map
** if the element is the last one in the row use {}, an empty list, as the value
Topological sort:
List sortedList;
for each key in the map:
if value.size() == 0 {
remove key from the map
add it the key to the sortedList
for each key' in the map:
if value'.contains(key)
value'.remove(key) (and update the map)
}
invert the sortedList
Test case :
the map for your first input will be:
{ A:{C,B} , C:{D} , D:{E,F} , E:{F} , F:{G} , G:{} , B:{F} }
Sort :
1 - G -> sortedList, map = { A:{C,B} , C:{D} , D:{E,F} , E:{F} , F:{} , B:{F} }
2 - F -> sortedList, map = { A:{C,B} , C:{D} , D:{E} , E:{} , B:{} }
3 - E -> sortedList, map = { A:{C,B} , C:{D} , D:{} }
4 - D -> sortedList, map = { A:{C,B} , C:{} }
5 - C -> sortedList, map = { A:{B} , B:{} }
6 - B -> sortedList, map = { A:{} }
6 - A -> sortedList, map = { }
sortedList = {G,F,E,D,C,B,A}
Invert - > {A,B,C,D,E,F,G}
do you think something like this would work?
var oMergedList = [];
function indexOfColumn(sColumnName)
{
for(var i = 0 ; i < oMergedList.length;i++)
if(oMergedList[i]==sColumnName)
return i;
return -1;
}
function getOrdinalIndex(sColumnName)
{
var i = 0;
for( ; i < oMergedList.length;i++)
if(oMergedList[i]>sColumnName)
break;
return i;
}
function merge(oPartial)
{
var nPreviousColumnPosition = -1;
for(var i = 0 ; i < oPartial.length;i++)
{
var sColumnName = oPartial[i] ;
var nColumnPosition = indexOfColumn(sColumnName);
if(nColumnPosition>=0)//already contained
{
if(nPreviousColumnPosition>=0 && nColumnPosition!=(nPreviousColumnPosition+1))//but inserted on wrong place
{
oMergedList.splice(nColumnPosition, 1);
nColumnPosition = nPreviousColumnPosition
oMergedList.splice(nColumnPosition, 0, sColumnName);
}
nPreviousColumnPosition = nColumnPosition;
}
else //new
{
if(nPreviousColumnPosition<0)//no reference column
{
nPreviousColumnPosition = getOrdinalIndex(sColumnName);
}
else// insert after previous column
nPreviousColumnPosition++;
oMergedList.splice(nPreviousColumnPosition, 0, sColumnName);
}
}
}
/* latest sample
merge(['A','C','E','G']);
merge(['A','D']);
merge(['C','D']);
*/
/* default sample
merge(['A','C','D','E','F']);
merge(['D','F','G']);
merge(['A','B','F']);
*/
/* fix order
merge(['A','B']);
merge(['A','C']);
merge(['A','B','C']);
*/
/* insert alphabetically
merge(['B']);
merge(['A']);
merge(['C']);
*/
document.body.innerHTML = oMergedList.join(',');
the only "undefined" parts are where to insert if you have no previous columns (I putted in firt position)
and second in the case A,B.. A,C the columns will be inserted when first seen
means A,B..A,C will give A,C,B .. and means A,C..A,B will give A,B,C
edited to use the current array position to fix
previous addition so if you add [A,C][A,B] you will get [A,C,B] but if you then pass [A,B,C]
the array will be fixed to reflect the new order
also when new columns appears and there is no reference column appends in alphabetical order
fixed the column correctioning par.. should now give you the correct result..
As described by JSON.org there is not such thing as a Json ordered keys:
An object is an unordered set of name/value pairs.
That being said, it becomes quite easy to merge objects as you don't need the order.
for (var attrname in obj2) { obj1[attrname] = obj2[attrname]; }
Source: How can I merge properties of two JavaScript objects dynamically?

Better solution to find a cell in an array

I have the following array:
var myArray = [
{
"id":1,
"name":"name1",
"resource_uri":"/api/v1/product/1"
},
{
"id":5,
"name":"name2",
"resource_uri":"/api/v1/product/5"
}
]
Each row is identified by it's unique id. I am quite new to Javascript and was wondering what was the best solution to find a cell based on id.
For example, for the id:5; my function must return:
findCell(myTable, id=5);
// this function must return:
{
"id":5,
"name":"name2",
"resource_uri":"/api/v1/product/5"
}
I'm quite afraid to do an ugly for loop... Maybe there is some built-in javascript function to perform such basic operations.
Thanks.
Yes there is a built-in function - filter. I would use it like this:
findCells(table, property, value) {
return table.filter(function (item) {
return item[property] === value;
});
}
findCells(myTable, "id", 5);
This is a bit modified version, of what you want: it can find all cells by the specified property name value.
Edit: using for loop to search the first occurence of the element is okay, actually:
findCell(table, id) {
var result = null;
for (var i = 0, cell = table[0], l = table.length; i < l; i++, cell = table[i]) {
if (cell.id === id) {
result = cell;
break;
}
}
return result;
}
findCell(myTable, 5);
Try this expression this might be helpful
var result = myArray.filter(function(element, index) { return element.ID == 5; });
filter() has two parameters
element - current row
index - current row index.
If you are going to stick with the array, the 'ugly' for loop is your best bet for compatibility. It's not that ugly when put in a function:
function findById(id) {
for(var i = 0; i < myArray.length; ++i) {
if(myArray[i].id === id) return myArray[i];
}
}
// Not checking return value here!
alert(findById(5).name);
Filter is another option if your concern is only with recent versions of browsers. It will return an array of values.
If your array is very large though, it would make sense to introduce some sort of index for efficient lookups. It adds an additional maintenance burden, but can increase performance for frequent lookups:
var index = {};
for(var i = 0; i < myArray.length; ++i) {
index[myArray[i].id] = i;
}
// Find element with id=5
alert(myArray[index[5]].name);
Example

Categories