I'm slowly learning JavaScript, and to this point I could figure everything out on my own. But I'm just spending waaay to much time at this problem.
Basically, I have two arrays, and if a number in the second array matches a number in the first array, it has to delete that number.
So the simplest solution I can think of is a for loop that loops through each property of the array, and if it doesn't match a number out of the second array, push it into a new array.
I have written this bit of code:
var arr = [1, 2, 3, 5, 1, 2, 3], newArr = [2, 3, 3];
var finalArr = [];
for (var i = 0; i < newArr.length; i++) {
if (arr[i] != newArr[0] && arr[i] != newArr[1] && arr[i] != newArr[2])
finalArr.push(arr[i]);
}
// ----> finalArr = [1]
The purpose is, it takes every value out of the array named "arr", compares it to a value of "newArr", and if the value doesn't match, push it into a new array.
Can anyone see the problem?
Thanks in advance!
var arr = [1, 2, 3, 5, 1, 2, 3], newArr = [2, 3, 3];
var finalArr = [];
for (var i = 0; i < arr.length; i++) {
if (arr[i] != newArr[0] && arr[i] != newArr[1] && arr[i] != newArr[2])
finalArr.push(arr[i]);
}
// result: [1,5,1]
This is your corrected code, just note that second array does not always need to contain three elements, you could loop through it as well with a for loop
The purpose is, it takes every value out of the array named "arr", compares it to a value of "newArr"...
You're not iterating over every value of arr, since you've newArr.length in your loop. You need to iterate till arr.length instead
...if the value doesn't match, push it into a new array.
Your code assumes newArr is always of length 3. Maybe it is. But, a more generic way is to use Array#indexOf which returns the first index at which a given element can be found in the array, or -1 if it is not present.
var arr = [1, 2, 3, 5, 1, 2, 3],
newArr = [2, 3, 3];
var finalArr = [];
for (var i = 0; i < arr.length; i++) {
if (newArr.indexOf(arr[i]) === -1) {
finalArr.push(arr[i]);
}
}
console.log(finalArr);
Related
There is a problem in FreeCodeCamp.I'm not here to search solution to that problem. While trying to solve the problem I found some lines of my code do not work. I can not understand why that is not working. So I am here to ask YOU, good people, to help me.
problem
There is a function. I will pass an array and a number to that function. And what I need to return is also an array.
The array is a multidimensional array.
what I want to do
First of all I want to check if the inner or subarray contains the number i passed while calling the function. If that contains i need the index of that number in that subarray. Then I want to delete the number from that subarray using splice(). At last I wanted to return an array where there are sub arrays in it but none of them contain the given number.
where i am stuck in
But I am stuck in finding the index of the number in sub arrays, how can i use splice() to delete the number? Is it possible to do this way? Do you have any better suggestion for me?
my code
where for the first for loop it just prints -1
for the second loop, it prints the index of the array,not the index of the subarray.
function filteredArray(arr, elem) {
let newArr = [];
// Only change code below this line
newArr = [...arr]
let L = arr.length;
for (let i = 0; i < L; i++) {
// -------1-----------
for (elem in newArr[i]) {
console.log(newArr[i].indexOf(elem));
}
}
console.log('first loop ends')
for (let i = 0; i < L; i++) {
// --------2---------
for (let j = 0; j < newArr[i].length; j++) {
if (newArr[i][j] == elem) {
console.log(newArr[i].indexOf(elem))
}
}
// Only change code above this line
return newArr;
}
}
console.log(filteredArray([[3, 2, 3], [1, 6, 3], [3, 13, 26], [19, 3, 9]], 3));
function filteredArray(arr, elem) {
let newArr = [];
// Only change code below this line
newArr = [...arr]
let L = arr.length;
// for (let i = 0; i < L; i++) {
// // -------1-----------
// for (elem in newArr[i]) {
// console.log(newArr[i].indexOf(elem));
// }
// }
console.log('first loop ends')
for (let i = 0; i < L; i++) {
// --------2---------
for (let j = 0; j < newArr[i].length; j++) {
if (newArr[i][j] == elem) {
console.log(newArr[i].indexOf(elem))
}
}
// Only change code above this line
return newArr;
}
}
console.log(filteredArray([[3, 2, 3], [1, 6, 3], [3, 13, 26], [19, 3, 9]], 3));
When removing values from an array you don't want to use a mutating method like .splice() -- the original array will change. If .splice() removes a number, the length of the array decreases and all indexes at and after the index of the removed number will shift (unless you replace that number instead). The non-mutating methods such as filter() and .map() makes a copy of the array and returns the copy leaving the original array intact. See this article for a easy reference of what mutates and what doesn't.
You could simplify the process of removing a given number from an array of arrays by using .map() on each sub-array and .filter() each sub-array with the condition of returning only numbers that do not equal the given number.
const data = [[3, 2, 3], [1, 6, 3], [3, 13, 26], [19, 3, 9]];
const filterCols = (target, arrArr) =>
arrArr.map(sub => sub.filter(num => num !== target));
console.log(filterCols(3, data));
I have an array like:
var myArray = [[1, 2, 3, 4], [5, 6], [7, 8, 9], [10]];
How I can reorder this array with the following rules:
myArray[0][0] to reduce size to 2 elements (values 1,2 stay, 3,4 goes to next array)
keep all values just move extra array elements to the next array, but all arrays need to keep the current number of elements except last
WHat I already try is:
function conditionalChunk(array, size, rules = {}) {
let copy = [...array],
output = [],
i = 0;
while (copy.length)
output.push( copy.splice(0, rules[i++] ?? size) )
return output
}
conditionalChunk(myArray, 3, {0:2});
but in that case, I need to put rules for all arrays in the array, I need to know a number of elements for all arrays in the array, and that's what I want to avoid.
Is there any elegant way to do that?
The requirements were not that clear and I don't know why you would do any of that but here you have your solution: I have written a function that you can use to limit the cardinality of the subArrays.
var myArray = [[1, 2, 3, 4], [5, 6], [7, 8, 9], [10]];
function reorderArray(arr) {
let buff = [];
let newArr = [];
let maxSubarrCardinality = 2;
//flatMapping second level elements
//in a single level buffer
for (subArr of arr) {
for (elem of subArr) {
buff.push(elem);
}
}
//Inserting elements one at the time
//into a new array
for (elem in buff) {
//when the new array is empty
//push in the new array the first empty subArray
if (newArr.at(-1) == undefined)
newArr.push([]);
//if the last subArray has reached
//the maxCardinality push a new subArray
else if (newArr.at(-1).length >= maxSubarrCardinality) {
newArr.push([]);
newArr.at(-1).push(elem);
}
//if none of the previous special cases
//just add the element to the last subArray of the newArray
else {
newArr.at(-1).push(elem);
}
}
return newArr;
}
myArray = reorderArray(myArray);
Steps
I used Array#flat, so I had to keep track of the indices and length of each item.
let i = 0;
let itemLength = array[0]?.length;
After flattening the array, I use Array#reduce to loop through the items, setting the initialValue to an empty array.
I get the last item in the array and check if its length has reached the maximum allowed (which should be the one set in the rules argument for that index or the size argument).
If the max hasn't been reached, I push the current item into the last array. If it has, I create a new array, with the item as the only element
array.flat().reduce((acc, cur) => {
if (acc.length === 0) acc.push([]); // Just for the first iteration
if (acc.at(-1).length < (rules[i] ?? size)) acc[acc.length - 1].push(cur);
else acc.push([cur]);
If then either decrement the value of itemLength, or set it to the length of the next array. and increment the i variable to the next index
itemLength = itemLength === 0 ? (array[++i] ?? []).length : --itemLength;
let array = [[1, 2, 3, 4], [5, 6], [7, 8, 9], [10]];
function conditionalChunk(array, size, rules = {}) {
let i = 0;
let itemLength = array[0]?.length;
return array.flat().reduce((acc, cur) => {
if (acc.length === 0) acc.push([]); // Just for the first iteration
if (acc.at(-1).length < (rules[i] ?? size)) acc[acc.length - 1].push(cur);
else acc.push([cur])
itemLength = itemLength === 0 ? (array[++i] ?? []).length : --itemLength;
return acc;
}, []);
}
console.log(JSON.stringify(conditionalChunk(array, 3, { 0: 2 })));
Here is what I came up with. Paste the code into the chrome console and test it.
var myArray = [[1, 2, 3, 4], [5, 6], [7, 8, 9], [10]];
//Getting initial lengths of inner arrays [4, 2, 3, 1]
var lengths = [];
myArray.forEach((arr) => {lengths.push(arr.length);});
// Extracting the elements of all the inner arrays into one array.
var allElements = [].concat.apply([], myArray);
// Updating the lengths of first and last inner arrays based on your requirement.
var firstArrLen = 2;
var lastArrLen = lengths[lengths.length -1] + (lengths[0] - 2)
lengths[0] = firstArrLen;
lengths[lengths.length -1] = lastArrLen;
// Initializing the final/result array.
var finalArr = [];
// Adding/Pushing the new inner arrays into the finalArr
for(var len of lengths) {
var tempArr = [];
for(var i=0; i<len; i++) {
tempArr.push(allElements[i]);
}
finalArr.push(tempArr);
for(var i=0; i<len; i++) {
// removes the first element from the array.
allElements.shift();
}
}
console.log(finalArr);
I have two one-dimensional arrays, a and b. a has values and b is empty. The length of a is an even number. I'd like to remove every other value from a and move them to b, in the same order as they were placed in a.
var a = [1, 2, 3, 4, 5, 6], b = [];
becomes
var a = [1, 3, 5], b = [2, 4, 6];
I figured that filter would do the trick but I'm not that happy with the performance of it since the average length of a is 300-400.
b = a.filter((i, idx) => {
return idx % 2 == 0;
});
a = a.filter((i, idx) => {
return idx % 2 == 1;
});
I've also been looking at lodash to see if that library had anything that might help me and the only function that's near what I'm looking for is _.chunk(array, \[size=1\]).
I appreciate any and all help to help me figure out a better, faster way to do this.
Since you mentioned lodash you could do this with _.partition:
let a = [1, 2, 3, 4, 5, 6];
let b = [];
let i = -1;
[a, b] = _.partition(a, (item) => i++ % 2);
console.log(a);
console.log(b);
<script src="https://cdn.jsdelivr.net/lodash/4.17.4/lodash.min.js"></script>
Partition's predicate is the identity function, which doesn't include the index of the item, so this comes with a compromise of an external index i.
Of course, you could always wrap this functionality into it's own function:
const splitEvenOdd = (array, i = -1) => _.partition(array, (item) => i++ % 2);
let a = [1, 2, 3, 4, 5, 6];
let b = [];
[a, b] = splitEvenOdd(a);
console.log(a);
console.log(b);
<script src="https://cdn.jsdelivr.net/lodash/4.17.4/lodash.min.js"></script>
Vanilla JS ES5, simple and clean.
var a = [1, 2, 3, 4, 5, 6], b = [];
for(var i = a.length-1; i >= 0; i--) {
if(i % 2 === 1) {
b.unshift(a.splice(i, 1)[0])
}
}
Basically, it is iterating through a backwards, and if the condition is true splicing the item und adding it as first item of b.
To loop through the source once, the values can be added to a specific array depending on the index. For example:
const source = [1, 2, 3, 4, 5, 6];
let arrs = [[],[]];
for(let i = 0; i< source.length; i++)
arrs[i%2].push(source[i]);
let [a,b] = arrs;
console.log(a);
console.log(b);
Alternatively, if it's important to alter the original arrays, a can be filled in a direct iteration, since the index being processed is always ahead of the one being filled:
let a = [1, 2, 3, 4, 5, 6], b= [];
for(let i = 0; i< a.length; i++)
(i % 2 ? b : a)[Math.floor(i/2)] = a[i];
a.splice(a.length/2);
console.log(a);
console.log(b);
The best performance you can get for this is 0(n) or linear time since you have to iterate through the entire array. What may help is reducing the number of loops
var a=[];
var b=[];
function splitArray(arr)
{
for (var i=0;i<arr.length;++i)
{
if (arr[i]%2 == 0)
b.push(arr[i]);
else
a.push(arr[i]);
}
}
What this does is reduces the number of times you have to iterate through the original array from 2 to 1
Hi I'm struggling to solve this problem. How to create a javascript function that takes any number of arrays as arguments, then returns an array of elements that only appear in one of the arrays. All items that appear in multiple arrays are removed. Getting nowhere with a solution, suspect I'm not approaching it in the right way, stumped!
Edit: the other question addresses eliminating duplicate values in one array, I need to compare x number of separate arrays and return the values that aren't duplicated between arrays. So ([5,6,7],[5,8,9]) returns [6,7,8,9].
function sym(args) {
var ans = [];
for(var i =0;i<arguments.length;i++){
var tempArr = arguments[i].filter(function(el){
var filtTrue = false;
for(var j = 0;j<arguments.length;j++){
if(Array.isArray(arguments[j]) && arguments[j] !== arguments[i]){
if(arguments[j].indexOf(el) === -1){
filtTrue = true;
}}
}
return filtTrue;
});
ans = ans.concat(tempArr);
}
return ans;
}
Here's one way to do it. The idea here is that you create a map for keeping counts of all the items in the array. You then cycle through each array, look up each value in the map and, if found, you increment its count. If not found, you set the count to 1. Then, when done with all the arrays, you collect any items that have a count of 1.
You weren't specific about what to do if an item appears more than once in the same array, but not in any other array. This first solution will not include that item (since it detects duplicates). It could be adapted (with a little more complexity) to allow that item if that was an issue (see 2nd code block below for that implementation).
function sym(/* pass one or more arrays here */) {
var ans = [], cnts = {};
//count all items in the array
for (var i = 0; i < arguments.length; i++){
arguments[i].forEach(function(item) {
if (cnts.hasOwnProperty(item)) {
// increase cnt
++cnts[item].cnt;
} else {
// initalize cnt and value
cnts[item] = {cnt: 1, val: item};
}
});
}
for (var item in cnts) {
if (cnts.hasOwnProperty(item) && cnts[item].cnt === 1) {
ans.push(cnts[item].val);
}
}
return ans;
}
If you want to include items that are present more than once in a single array, but not present in any other array, then you can use this slightly more complicated adaptation:
function sym(/* pass one or more arrays here */) {
var ans = [], cnts = {}, currentMap;
//count all items in the array
for (var i = 0; i < arguments.length; i++){
currentMap = {};
arguments[i].forEach(function(item) {
// if we haven't already counted this item in this array
if (!currentMap.hasOwnProperty(item)) {
if (cnts.hasOwnProperty(item)) {
// increase cnt
++cnts[item].cnt;
} else {
// initalize cnt and value
cnts[item] = {cnt: 1, val: item};
}
}
// keep track of whethere we've already counted this item in this array
currentMap[item] = true;
});
}
// output all items that have a cnt of 1
for (var item in cnts) {
if (cnts.hasOwnProperty(item) && cnts[item].cnt === 1) {
ans.push(cnts[item].val);
}
}
return ans;
}
Working demo: http://jsfiddle.net/jfriend00/bete5k3n/
I know this is increadibly late but this is another way to do it. Maybe not the most rigorous one but certainly creative. The method Array.symmetricDifference() expects any number of arguments and returns the symmetric difference of those arguments.
Array.prototype.symmetricDifference = function() {
var args = [];
// copy arguments into a real array and get rid of duplicates with filter
for(var i = 0; i < arguments.length; i++) {
args[i] = arguments[i];
args[i] = args[i].filter(function(item, pos, self) {
return self.indexOf(item) == pos;
});
}
var diff = args[0];
// iterate through every arguments in args.
// concatenate the first two arguments to form a new set.
// now every number in this new set that was contained in both arguments
// from before will be contained at least twice in this new set.
for(var j = 1; j < args.length; j++) {
//sort the new set so that duplicates are next to each other.
diff = diff.concat(args[j]).sort();
var index = 0;
// now iterate through the new set and delete both duplicates if you
// find any. Otherwise proceed to the next index.
while(index < diff.length) {
// if duplicate is found delete both, otherwise look at next index.
diff[index] === diff[index + 1] ? diff.splice(index, 2) : index++;
}
}
return diff;
};
You can invoke that method on any array or create a new one and invoke it on that one like this for example:
// take any number of arrays
var a = [3, 3, 3, 2, 5];
var b = [2, 1, 5, 7];
var c = [3, 4, 6, 6];
var d = [1, 2, 3];
var e = [5, 3, 9, 8];
var f = [1];
// invoke the method on "solution" with any number of arguments
// and store it in solution.
var solution = solution.symmetricDifference(a,b,c,d,e,f);
console.log(solution); // [1, 2, 4, 5, 6, 7, 8, 9]
I hope this helps!
Finding unique items in multiple arrays
function uniqueItemsInArrays(...args){
let allItems = [];
allItems = allItems.concat(...args)
return allItems.filter(function(item, pos, self) {
return self.indexOf(item) === pos && self.indexOf(item,pos+1) === -1;
});
}
uniqueItemsInArrays( [1, 5, 1, 8, 1, 2],
[2, 2, 9, 3, 5],
[1, 4, 7, 6] );
The above code uses ES6 rest parameters to access all the arrays passed as arguments. Then using the concat() function, I am joining all the individual arrays to a single array. Finally, filter function has been used to identify and return the unique items from this array. The logic here is to find out the first index of the current item and if there are no more occurrence from the first index then we return that item.
Here is the way I am using to return duplicate elements.. But I am facing most dangerous performance issues like browser close etc when my array have large number of items with long texts..
var arr = [9, 9, 111, 2, 3, 4, 4, 5, 7];
var sorted_arr = arr.sort();
var results = [];
for (var i = 0; i < arr.length - 1; i++) {
if (sorted_arr[i + 1] == sorted_arr[i]) {
results.push(sorted_arr[i]);
}
}
alert(results);
Please suggest me a best way of doing this
i don't get exactly what you want, but if you need to return duplicates you could use a cache object. this works with number or string or whatever.
var arr = [9, 9, 111, 2, 3, 4, 4, 5, 7];
var cache = {};
var results = [];
for (var i = 0, len = arr.length; i < len; i++) {
if(cache[arr[i]] === true){
results.push(arr[i]);
}else{
cache[arr[i]] = true;
}
}
console.log(results);//returns an array with 9 and 4
Of course you can do other things like deleting multiple items etc. etc.
EDIT - i've written a blog entry on how to remove duplicates from an array
If you have array filter, you also have indexOf and lastIndexOf,
and you can return the duplicates without doing the sort.
var results, arr= [9, 9, 111, 2, 3, 4, 4, 5, 4, 7];
if(arr.filter){
results= arr.filter(function(itm, i){
return arr.lastIndexOf(itm)== i && arr.indexOf(itm)!= i;
});
}
else// use your loop method
alert(results)
/* returned value: (Array)
9,4
*/
Assuming Nicola's solution doesn't work for you (since it uses about as much memory as the original solution: two elements stored per element in the input, worst-case), you can use the slower process of repeatedly searching your input.
This requires the Array.indexOf method from ECMAScript 5. A lot of browsers have it. For alternatives, see How do I check if an array includes an object in JavaScript?.
var arr = [9, 9, 111, 2, 3, 4, 4, 5, 7];
var results = [];
for (var i = 0, len = arr.length - 1; i < len; i++) {
if((results.indexOf(arr[i]) == -1) && (arr.indexOf(arr[i], i + 1) != -1)) {
results.push(arr[i]);
}
}
console.log(results);
This uses no more memory than the input arr plus the output results, but it's an O(N^2) algorithm and doesn't have to modify arr.
Your method relies on a sort, which may or may not be one reason you run out of space/time.
The canonical way to remove duplicates is to keep a hash map of the keys (an object in JS). The object keys you get back won't necessarily be in the order you want; you don't specify if you want the results ordered as well, but they are now.
You could null out the original array, since you no longer require it; when it gets collected is up to the JS engine though.
You could remove duplicates "in place" by keeping a "current index" into the sorted array, and increment it only when you move a non-duplicated element "down" from the counter index, then truncate the array that you return.
Combining the last two techniques should mean that in general you'll only have a single array with a valid reference.
Edit Example. Setting length explicitly, as .slice() creates a new array.
var have = {};
var arr = [9, 9, 111, 2, 3, 4, 4, 5, 7];
arr = arr.sort();
for (var rIdx = 0, i = 0; i < arr.length; i++) {
if (have[arr[i]]) {
arr[rIdx++] = arr[i];
} else {
have[arr[i]] = true;
}
}
arr.length = rIdx;
console.log(arr);