Deleting Element After Pushing - javascript

If I have an array where I am pushing certain elements to a second array- how can I delete those elements from the first array after pushing them to the second? Here is sample code:
for(var a = 0; a < arr.length; a+=1){
if(arr[a].length == 4){
other.push(arr[a]);
}
}
In other words, I know longer want elements arr[a] to be in arr if they have been pushed to other.

Just do a splice on that original array index to remove that element if you no longer require it.
for(var a = 0; a < arr.length;){
if(arr[a].length == 4){
other.push(arr[a]);
arr.splice(a, 1);
}
else {
a += 1;
}
}

This seems fine:
for(var a = 0, length=arr.length; a < length; a++){
if(arr[a].length == 4){
other.push(arr[a]);
arr.splice(a,1);
}
}

Write a function which takes an input array, and a function to determine if an element should be moved. It returns a two-element array, containing the modified input, and the new array into which elements have been extracted.
function extractIf(array, condition) {
return [
array.filter(not(condition)),
array.filter( condition)
];
}
// Specify which elements are to be extracted/moved.
function condition(elt) { return elt.length === 4; }
// Little helper function to invert a function.
function not(fn) { return function() { return !fn.apply(this, arguments); }; }
Invoke this as:
var results = extractIf(arr, condition);
arr = results[0];
other = results[1];
underscore solution
If you are willing to use underscore, you could group the input by the true/false value of the condition:
var groups = _.groupBy(arr, function(elt) { return elt.length === 4; })
Your original array with the elements removed will be in groups.false, and the other array in groups.true.

Related

An element of an array doesn't pass a test

I am writing a function that loops through an array and tests each element for a condition. If it is false, the element is dropped. If it is true, the function returns the rest of the array.
Here is my code:
function dropElements(arr, func) {
// Loop through the array
for(var i = 0; i < arr.length; i++){
// if the current element passes the test, return the rest of the array
if (func(arr[i])){
return arr;
// otherwise remove the element
} else {
arr.shift();
}
}
// if no conditions were met, return empty array
return arr;
}
When the loop reaches i = 1, we have arr[1] = 2.
Since 2 is not >= 3, I do not understand why it is not dropped.
So, why does the following call
dropElements([1,2,3,4], function(n) {return n >= 3; });
return [2,3,4] instead of [3,4] ?
Thanks.
Because when you "shift" it removes the index and the other indexes shift down to fill in the hole. So when you remove the first one, the second index is now one. And since you increment i, you skip the index that was moved down.
So to get around your issue, you would need to reduce i
else {
arr.shift();
i--;
}
Or you can do another solution where you find the index and than just splice the array.
From MDN doc:
The shift() method removes the first element from an array and returns that element. This method changes the length of the array.
In your code this means that you have to sync the index in the loop. You can simply decrease the index by 1
function dropElements(arr, func) {
// Loop through the array
for(var i = 0; i < arr.length; i++){
// if the current element passes the test, return the rest of the array
if (func(arr[i])){
return arr;
// otherwise remove the element
} else {
arr.shift();i--;
}
}
// if no conditions were met, return empty array
return arr;}
This will solve your problem
It has to do with the order in which you are shifting the array. Take into comparison the following:
function dropElements(arr, func) {
if (!Array.isArray(arr) && arr.length == 0) {
return [];
}
while (!func(arr[0]) && arr.length > 0) {
arr.shift();
}
return arr;
}
epascarello's answer should be accepted as the correct one, however here is an updated code block for what you need:
function dropElements(arr, func) {
// Loop through the array
for(var i = 0; i < arr.length; i++){
// if the current element passes the test, remove it from the array
if (func(arr[i])){
return arr;
} else {
arr.shift();
i--;
}
}
// Return the array
return arr;
}
var arr = [1,2,3,4,2];
dropElements(arr, function(num) {
return num >= 3;
});
This outputs [3, 4, 2] (this is because we assume that the array is ordered when we loop through it and return from the function when the test function is satisfied once). If you want to loop on an unordered array, simply remove the return arr in the loop, just note that this will run at O(n) opposed to O(log n)

compare 2d array with 1d array in javascript

var player1=["t1", "t9", "t7", "t8", "t2"];
var player2=["t5", "t3", "t4", "t6"];
var winmoves=[[t1,t2,t3],[t4,t5,t6],[t7,t8,t9],[t1,t4,t7],[t2,t5,t8],[t3,t6,t9],[t1,t5,t9],[t3,t5,t7]];
else if (moves>=3 && moves<=9) {
moves+=1;
if (turn==1) {
var i=0;
turn=2;
x.innerHTML="<img src='x.png'/>";
player1.push(x.id);
while(i<=moves){
if (winmoves[i] in player1) {
alert("player1 wins");
document.getElementById("player1").innerHTML=1;
}
i+=1;
}
}
i have 1d array player1 and 2d array winmoves in javascript and i want to check that w[0]'s all values are present in p and so on for w[1],w[2],etc.
if condition with (winmoves[i] in player1) is not working.
i don't know if i am writing this write.
help me guys i am stuck here how can i do so.
It is not working even after i have made these changes.
else if (moves>=3 && moves<9) {
moves+=1;
if (turn==1) {
var i=0;
turn=2;
x.innerHTML="<img src='x.png'/>";
player1.push(x.id);
while(i<=moves){
mapped1 = winmoves.map(a1 => a1.every(e1 => player1.includes(e1)));
if (mapped1[i]) {
alert("player1 wins");
document.getElementById("player1").innerHTML=1;
}
i+=1;
}
}
else if (turn==2) {
turn=1;
var i=0;
x.innerHTML="<img src='o.png'/>";
turn=1;
player2.push(x.id);
while(i<=moves)
{
mapped2 = winmoves.map(a => a.every(e => player2.includes(e)));
if (mapped2[i]) {
alert("player2 wins");
document.getElementById("player2").innerHTML=1;
}
i+=1;
}
}
}
I would simply do this with a simple invention of Array.prototype.intersect() and the rest is such a straightforward single liner.
Array.prototype.intersect = function(a) {
return this.filter(e => a.includes(e));
};
var player1 = ["t1","t2","t3","t5","t7"],
winmoves = [["t1","t2","t3"],["t4","t5","t6"],["t7","t8","t9"],["t1","t4","t7"],["t2","t5","t8"],["t3","t6","t9"],["t1","t5","t9"],["t3","t5","t7"]];
filtered = winmoves.filter(a => a.intersect(player1).length == a.length);
mapped = winmoves.map(a => a.intersect(player1).length == a.length);
console.log(filtered);
console.log(mapped);
OK we have a generic Array method which finds the intersection of two arrays. (inbetween the array it's called upon and the one provided as argument) It's basically a filter checking each item of first array to see whether it is included in the second array. So we filter out the items exist in both arrays.
To obtain the filtered array we utilize Array.prototype.filter() again. This time our first array is winmoves which includes arrays that we will check for each the intersection with player1 array. If the intersection length is equal to winmove's item's length that means all elements of winmove's item is existing in the player1 array. So we return that array item to the filtered.
Specific to your case without using an intersect method you can utilize Array.prototype.every() as follows;
var player1 = ["t1","t2","t3","t5","t7"],
winmoves = [["t1","t2","t3"],["t4","t5","t6"],["t7","t8","t9"],["t1","t4","t7"],["t2","t5","t8"],["t3","t6","t9"],["t1","t5","t9"],["t3","t5","t7"]];
filtered = winmoves.filter(a => a.every(e => player1.includes(e)));
mapped = winmoves.map(a => a.every(e => player1.includes(e)));
console.log(filtered);
console.log(mapped);
What you could do is use nested for loops
for(var j = 0, j < elemInP, j++){
int flag = 0;
for(var x = 0, x < elemInWx, x++){
for(var y = 0, y < elemInWy, y++){
if(p[j] == w[x][y]){
flag = 1;
/*There is no need to run this loop once the value has been found*/
break;
}
}
if(flag){
/*If we have found the value no need to keep looking*/
break;
}
}
if(!flag){
print p[j] is not in w;
}
}
This is just a general idea of one way to compare the two arrays. The actual syntax of the code will need to be edited to work in JavaScript as this is just basic pseudocode. Flag is just a variable that holds if the value was found or not and is reset for each new value. For efficiency, you could have a break/return after setting flag = 1, but this is

Remove data from an array comparing it to an other array

I am trying to compare the items in "item" array and the copyofOpList array to retrieve the data occurrences in copyofOpList
this is my try:
var _deleteUsedElement1 = function(item) {
for (var i = 0; i < item.length-1; i++){
for (var j = 0; j< $scope.copyofOpList.length-1; j++){
if (item[i].operationCode == $scope.copyofOpList[j].code) {
$scope.copyofOpList.splice(j, 1);
} } } };
$scope.compareArrays = function() {
...Get data from web Service
_deleteUsedElement1(item);
}
the copyofOpList array has 14 elements,and the item array has 2 array
but my code deletes only one occurrence (the first),so please how can I correct my code,to retrieve any occurances in the copyofOpList array comparing to the item array
thanks for help
I'd try to avoid looping inside a loop - that's neither a very elegant nor a very efficient way to get the result you want.
Here's something more elegant and most likely more efficient:
var item = [1,2], copyofOpList = [1,2,3,4,5,6,7];
var _deleteUsedElement1 = function(item, copyofOpList) {
return copyofOpList.filter(function(listItem) {
return item.indexOf(listItem) === -1;
});
};
copyofOpList = _deleteUsedElement1(item, copyofOpList);
console.log(copyofOpList);
//prints [3,4,5,6,7]
}
And since I just noticed that you're comparing object properties, here's a version that filters on matching object properties:
var item = [{opCode:1},{opCode:2}],
copyofOpList = [{opCode:1},{opCode:2},{opCode:3},{opCode:4},{opCode:5},{opCode:6},{opCode:7}];
var _deleteUsedElement1 = function(item, copyofOpList) {
var iOpCodes = item.map(function (i) {return i.opCode;});
return copyofOpList.filter(function(listItem) {
return iOpCodes.indexOf(listItem.opCode) === -1;
});
};
copyofOpList = _deleteUsedElement1(item, copyofOpList);
console.log(copyofOpList);
//prints [{opCode:3},{opCode:4},{opCode:5},{opCode:6},{opCode:7}]
Another benefit of doing it in this manner is that you avoid modifying your arrays while you're still operating on them, a positive effect that both JonSG and Furhan S. mentioned in their answers.
Splicing will change your array. Use a temporary buffer array for new values like this:
var _deleteUsedElement1 = function(item) {
var _temp = [];
for (var i = 0; i < $scope.copyofOpList.length-1; i++){
for (var j = 0; j< item.length-1; j++){
if ($scope.copyofOpList[i].code != item[j].operationCode) {
_temp.push($scope.copyofOpList[j]);
}
}
}
$scope.copyofOpList = _temp;
};

Array Splice - Javascript

Its a very small issue and for the life of me I can't figure out what it is. My brain has locked itself from thinking. I need someone else to have a look this code.
The output of the code should be: [1,0,0,0]
UPDATE:
The function should be able to read an array of numbers and if it finds any zeros within the array it should move them to the end of the array.
The output of the code keeps coming as: [0,1,0,0]
var arrNum = [0,0,0,1];
function test() {
for(var i=0; i<arrNum.length; i++){
if(arrNum[i] == 0){
arrNum.splice(i,1)
arrNum.splice(arrNum.length, 1, 0)
}
}
return alert(arrNum)
}
Here is a working plunker.
Apologies for this, I know the issue is something very small but my brain has stopped working now and I need a fresh pair of eyes.
With the way you have it written, you need to loop in the reverse order. You end up skipping indexes when you remove the index. Looping in the reverse direction keeps you from skipping them.
for(var i=arrNum.length-1; i>=0; i--){
You can use unshift() to insert at beginning of an array and push() to the end...
var arrNum = [0,0,0,1];
var output = [];
function test()
{
for(var i=0; i<arrNum.length; i++)
{
if(arrNum[i] == 0)
output.push(0);
else
output.unshift(arrNum[i]);
}
return alert(output)
}
var arrNum = [0,0,0,1];
var result = [];
arrNum.forEach(function(v) {
!!v ? result.unshift(v) : result.push(v);
});
console.log(result);
You are iterating with index i = 0,1,2,3 and at the same time removing first elements of array. So your iteration can not see the 1, it jumps over as it is moved to already iterated index. Easiest would be to just reverse the loop to bypass the issue.
var arrNum = [0,0,0,1];
function test() {
for(var i= arrNum.length; i >= 0; i--){
if(arrNum[i] == 0){
arrNum.splice(i,1)
arrNum.splice(arrNum.length, 1, 0)
}
}
return alert(arrNum)
}
Prefer built-in functions every time possible.
var output = [];
[0,0,0,1].forEach(function(num) {
if(num == 0) output.push(0);
else output.unshift(num)
})
Why don't you use a temporary array to help? The problem with your code is that the splice() function modifies the original array, and you are doing it inside the loop.
The code below produces what you need:
var arrNum = [0,0,0,1];
var arrResult = new Array();
function test() {
for(var i=arrNum.length-1; i>=0; i--)
{
arrResult.push(arrNum[i]);
}
arrNum = arrResult;
return alert(arrNum);
}
With another array to store the new values, you gain flexibility to do whatever you need with the data of the first array.
A nice little way using Objects - busy learning them so just posting a variation of deligation
var methods = {
moveZero: function(arr){
//console.log(arr);
var newArr = [];
for(var i = 0; i < arr.length; i++){
if(arr[i] === 0){
newArr.push(arr[i]);
}else{
newArr.unshift(arr[i]);
}
}
console.log(newArr);
}
}
var arrNum = Object.create(methods);
arrNum.moveZero([0,0,50,56,85,0,0,43,10,0,1]);
JSFiddle - https://jsfiddle.net/ToreanJoel/qh0xztgc/1/
The problem was you are modifying an array while looping over it in if statement.
Here is a working plunker of your example.
var len = arrNum.length;
var index = 0;
while(len) {
if(arrNum[index] == 0) {
arrNum.splice(index,1);
arrNum.push(0);
} else {
++index;
}
--len;
}
As the operation you want to do is actually sorting, for readability and compactness of code maybe you should be doing this instead:
var arrNum = [0,1,0,0];
arrNum.sort(function(a, b) {
return a == 0 ? 1 : 0;
});
It can contain any number and will keep order of others than 0

Word frequency for array of key/values on javascript

I'm trying to implement a piece of code on javascript to analyse word/frequency on a given string. My objective is to return a array as the following:
[{text: firstword, size:3 },{text:secondword , size:5 },{text: nword, size: 1},...]
I implemented the following code but I'm running out of memory, so I don't really know if its ok or not.
function wordFrequency(txt){
var wordArray = txt.split(/[ .?!,*'"]/);
var newArray = [];
$.each(wordArray, function (ix, word) {
if (newArray.length >= 1){
newArray.some(function (w){
if (w.text === word){
w.size++;
} else {
newArray.push({text: word, size: 1});
}
});
} else {
newArray.push({text: word, size: 1});
}
});
return newArray;
}
Array.prototype.some expects the given callback to return true or false and returns true as soon as your callback returns true for a given element, otherwise it returns false.
So some iterates over all elements, with your given callback, and your callback checks if the given element text equals the search word and if not adds a new object. Introducing a new element the some function can iterate over.
So to make this clear, for every word thats in the newArray before the word you're searching, you're adding a new object containing your word.
Suppose your newArray looks like this:
[{word:"test"},{word:"another"},{word:"one"},{word:"more"}]
after calling your function for the word even it looks like this:
[{word:"test"},{word:"another"},{word:"one"},{word:"more"},{word:"even"},{word:"even"},{word:"even"},{word:"even"}]
Using Array.prototype.filter would be the better approach here, finding you the matching element, note that I also replaced $.each with Array.prototype.forEach:
function wordFrequency(txt){
var wordArray = txt.split(/[ .?!,*'"]/);
var newArray = [], wordObj;
wordArray.forEach(function (word) {
wordObj = newArray.filter(function (w){
return w.text == word;
});
if (wordObj.length) {
wordObj[0].size += 1;
} else {
newArray.push({text: word, size: 1});
}
});
return newArray;
}
document.write(JSON.stringify(wordFrequency("count everything, count all the words, count all the words!").sort(function(a,b){return a.size<b.size})).split("},").join("}<br/>"));
It would be simpler and far more efficient to create a direct map from word to frequency, and only afterwards convert that to your array structure. Given an array words create a map of the words:
var freq = words.reduce(function(p, c) {
p[c] = (p[c] || 0) + 1;
return p;
}, {});
and the convert that map into your array:
var array = Object.keys(freq).map(function(key) {
return { text: key, size: freq[key] };
});
To tell the frequency all you need is a hash map approach. Your algorithm is quadratic, since the some method is nested in the each method, so you're always looping over the newArray just to find an entry and increment the size.
A map approach is easily achievable using a JavaScript object. It also gives you constant look-up time, which is better performance than the nested loops approach.
Try this approach instead:
function wordFrequency(txt){
var wordArray = txt.split(/[ .?!,*'"]/);
var map = {};
$.each(wordArray, function(ix, word) {
// skip empty results
if (!word.length) {
return;
}
// add word to map
if (!map[word]) {
map[word] = 0;
}
map[word]++;
});
return map;
}
To use the function:
var text = "hello!world*hello foo 'bar'foo";
var result = wordFrequency(text);
// iterate over results
Object.keys(result).forEach(function(w) {
console.log(w + ": " + result[w]);
});
// or use for...in
for (var w in result) {
console.log(w + ": " + result[w]);
}
If you really wanted to, you could then map the result into your desired array format with text and size properties:
var mappedResult = Object.keys(result).map(function(w) {
return { text: w, size: result[w] };
});
console.log(mappedResult);
Also, depending on your target browsers, you might consider using the array forEach instead of the jQuery $.each, similar to what I did with the Object.keys portion.
Here's the JSBin example.
You would probably want to avoid any iterations on duplicate elements and keep your results array unique. Since any of the iterators of Array.prototype will include each of the elements, they might not be the ideal solution for this. Sometimes plain old loops do the job best ...
(You may also want to expressively escape any special characters in your regular expression).
function wordFrequency(txt) {
var words = txt.split(/[ \.\?!,\*'"]+/),
seen = [];
for (var i = 0; i < words.length; i++) {
var w = words[i],
found = false;
for (var j = 0; j < seen.length; j++) {
if (w === seen[j].text) {
seen[j].size++;
found = true;
break;
}
}
if (!found) seen.push( { text: w, size: 1 } );
}
return seen;
}
(Note that the inner for-loop isn't visited for the first word, so the first word will be pushed to the seen-stack and the inner for-loop will start with the second word compared to the first one. Only words that we haven't seen already are added to the seen-stack, making it an array of unique elements.)
And here is the equivalent using Array.prototype.forEach() and Array.prototype.indexOf(), but we have to add another intermediate results stack for the latter one. So we'll have to add another iteration to produce the final result. (We wouldn't have to do this using Array.prototype.findIndex(), but this is not a standard method.)
function wordFrequency2(txt) {
var words = txt.split(/[ \.\?!,\*'"]+/),
seen = [],
freq = [];
// get frequencies
words.forEach(function (w) {
var idx = seen.indexOf(w);
if (idx >= 0) {
freq[idx]++;
}
else {
seen.push(w);
freq.push(1);
}
});
// produce the results array
var r = [];
seen.forEach(function (w, idx) {
r.push( { text: w, size: freq[idx] } );
});
return r;
}
Putting optimization into account, the first version using explicit loops will be probably performing faster ...
var words = (function(){
var sWords = document.body.innerText.toLowerCase().trim().replace(/[,;.]/g,'').split(/[\s\/]+/g).sort();
var iWordsCount = sWords.length; // count w/ duplicates
// array of words to ignore
var ignore = ['and','the','to','a','of','for','as','i','with','it','is','on','that','this','can','in','be','has','if'];
ignore = (function(){
var o = {}; // object prop checking > in array checking
var iCount = ignore.length;
for (var i=0;i<iCount;i++){
o[ignore[i]] = true;
}
return o;
}());
var counts = {}; // object for math
for (var i=0; i<iWordsCount; i++) {
var sWord = sWords[i];
if (!ignore[sWord]) {
counts[sWord] = counts[sWord] || 0;
counts[sWord]++;
}
}
var arr = []; // an array of objects to return
for (sWord in counts) {
arr.push({
text: sWord,
frequency: counts[sWord]
});
}
// sort array by descending frequency | http://stackoverflow.com/a/8837505
return arr.sort(function(a,b){
return (a.frequency > b.frequency) ? -1 : ((a.frequency < b.frequency) ? 1 : 0);
});
}());
(function(){
var iWordsCount = words.length; // count w/o duplicates
for (var i=0; i<iWordsCount; i++) {
var word = words[i];
console.log(word.frequency, word.text);
}
}());

Categories

Resources