I have an array
var arr = [{"id":"1","name":"One"},{"id":"2","name":"Two"}]
I push to the array
arr.push(X)
But how can I remove for example {"id":"1","name":"One"} from this array by name?
In plain javascript, you have to search through the array looking for a name match in each object and then remove that object:
function removeFromArrayByName(arr, name) {
for (var i = 0; i < arr.length; i++) {
if (arr[i].name === name) {
arr.splice(i, 1);
return;
}
}
}
Or if there might be more than one match and you want to remove all the matches there are, you can do this (does a backward traversal and doesn't return when it finds a match):
function removeFromArrayByName(arr, name) {
for (var i = arr.length - 1; i >= 0; i--) {
if (arr[i].name === name) {
arr.splice(i, 1);
}
}
}
Or, you could even make it more generic where you pass in the property name to search too:
function removeFromArrayByName(arr, prop, val) {
for (var i = arr.length - 1; i >= 0; i--) {
if (arr[i][prop] === val) {
arr.splice(i, 1);
}
}
}
The question is for plain js but if you use jquery, you can write a function like this:
function removeByName(arr, key){
return $.grep(arr, function (n,i) {
return n.name != key;
});
}
In your case, I will call removeByName(arr,'One');
Related
I have been working on this for a while now and I have no idea why this isn't working. I've read over it a few times and I can't seem to find the problem in my code.
I thought if(arr[i] === item) would check if the current element is equal to the item parameter and if true, return the index.
const lastIndexOf = (arr, item) => {
if(arr.includes(item)){
for(let i = arr.length - 1; i >= 0; i--){
if(arr[i] === item){
return i;
} else if (arr.includes(i) === false){
return Number('-1');
}
}
} else if (!arr.length){
return Number('-1');
} else {
return Number('-1');
}
}
console.log(lastIndexOf([5,5,5,5,0],5));
Remove the else if branch, as it is checking if the array contains the current index, which is nonsensical. Also, just use -1 instead of Number('-1').
const lastIndexOf = (arr, item) => {
if(arr.includes(item)){
for(let i = arr.length - 1; i >= 0; i--){
if(arr[i] === item){
return i;
}
}
return -1;
} else {
return -1;
}
}
console.log(lastIndexOf([5,5,5,5,0],5));
You are currently passing through the array twice, once using Array#includes and once with your for loop, which is suboptimal. You can simply loop over the array backwards once, like you are currently doing, and return -1 at the end of the function, since it will only reach there if the item has not been found.
const lastIndexOf = (arr, item) => {
for(let i = arr.length - 1; i >= 0; i--){
if(arr[i] === item){
return i;
}
}
return -1;
}
console.log(lastIndexOf([5,5,5,5,0],5));
The Array#lastIndexOf method already exists, so there is no need to reinvent the wheel, unless you are trying to make a polyfill.
console.log([5,5,5,5,0].lastIndexOf(5));
The problem is here:
} else if (arr.includes(i) === false){
You're asking if any of the array members equals the current index. Because 4 (your first checked index) is not accounted for in that array, it returns.
Seems like you don't need that else if at all.
const lastIndexOf = (arr, item) => {
if(arr.includes(item)){
for(let i = arr.length - 1; i >= 0; i--){
if(arr[i] === item){
return i;
}
}
} else {
return -1;
}
}
console.log(lastIndexOf([5,5,5,5,0],5));
Also, the last, outer else if and else branches yield the same result, so the else if is extraneous, so I removed it.
And Number('-1') is an unnecessarily roundabout way of returning -1.
And actually, your .includes() check seems redundant. It's going to have to iterate before you iterate. Might as well just do it once.
const lastIndexOf = (arr, item) => {
for(let i = arr.length - 1; i >= 0; i--){
if(arr[i] === item){
return i;
}
}
return -1;
}
console.log(lastIndexOf([5,5,5,5,0],5));
I'm trying to rewrite _.each and _.indexOf and it is throwing me for a loop.
My each function takes either an object or an array and passes tests set up.
_['each'] = function(collection, iterator) {
if (Array.isArray(collection) === false && typeof(collection) === 'object') {
var values = Object.values(collection);
var keys = Object.keys(collection);
for (let i = 0; i < values.length; i++) {
iterator(values[i], keys[i], collection, i);
}
} else {
for (let i = 0; i < collection.length; i++) {
iterator(collection[i], i, collection);
}
}
}
So I'm assuming this code is alright since it passes a preset test, but I'm not even sure about that. My question is, how would I write an indexOf() function that also uses the each() function? Each will run a function for every element and won't break, right? And I can't access the index via collection[i] because i is undefined in indexOf's scope. What am I missing?
pseudo code, check if you need the full javascript code :
indexOf = function(element, array){
// found index
index = -1;
// for each element, compare element, store index if found
each array (function(e, i){
// use cmp function to deep compare object/array as values
if(e === element)
{
// remove condition for lastIndexOf
if(index == -1)
{
index = i;
}
}
});
// return found index
return index;
}
I am trying to solve the below problem without any functional programming.
Question: Write a function called findShortestWordAmongMixedElements().
Given an array, findShortestWordAmongMixedElements() returns the shortest string within the given array.
Notes:
If there are ties, it should return the first element to appear in the given array.
Expect the given array to have values other than strings.
If the given array is empty, it should return an empty string.
If the given array contains no strings, it should return an empty string.
var output = findShortestWordAmongMixedElements([4, 'two', 2, 'three']);
console.log(output); // --> 'two'
This is my answer so far:
function findShortestWordAmongMixedElements(arr) {
// your code here
var shortestString = '';
if (arr.length > 0) {
for (var i = 0; i < arr.length; i += 1) {
if (typeof arr[i] === 'string' ) {
var length = arr[i].length;
if (arr[i].length < shortestString.length) {
shortestString = arr[i];
} else {
shortestString = arr[length.indexOf(arr[i].length)];
}
}
}
}
return shortestString;
}
or perhaps I should have done it this way:
function findShortestWordAmongMixedElements(arr) {
// your code here
var shortestString = '';
if (arr.length > 0) {
for (var i = 0; i < arr.length; i += 1) {
if (typeof arr[i] === 'string' ) {
var length = arr[i].length;
if (arr[i].length < shortestString.length) {
shortestString = arr[i];
}
if (arr[i].length === shortestString.length) {
shortestString = arr[length.indexOf(arr[i].length)];
}
}
}
}
return shortestString;
}
Nothing prints out in the 2nd function. Is my indexOf not correct?
function findShortestWordAmongMixedElements (arr) {
return arr.filter(d => typeof d == 'string').sort(d => -d.length)[0] || ''
}
I am trying to solve a freeCodeCamp exercise and have gotten stuck. The goal of the exercise is this: Make a function that looks through an array of objects (first argument) and returns an array of all objects that have matching property and value pairs (second argument). Each property and value pair of the source object has to be present in the object from the collection if it is to be included in the returned array.
So what I did, was to make an array of the key pairs of the collection, and another array with the key pairs of the source. The I nested for-loops in order to find matching keys, and if those keys are found, then compare the properties.
But somehow, my code returns no matches.
var collection = [{
first: "Romeo",
last: "Montague"
}, {
first: "Mercutio",
last: null
}, {
first: "Tybalt",
last: "Capulet"
}];
var source = {
last: "Capulet"
};
var collectionKeys = [];
for (var i = 0; i < collection.length; i++) {
collectionKeys.push(Object.keys(collection[i]));
}
var sourceKeys = Object.keys(source);
//for every key pair
for (var t = 0; t < collectionKeys.length; t++) {
//for every key in key pair
for (var x = 0; x < collectionKeys[t].length; x++) {
//for every key in search
for (var y = 0; y < sourceKeys.length; y++) {
//see if a key matches
if (sourceKeys[y] == collectionKeys[t][x]) {
//see if the value matches
if (collection[collectionKeys[t][x]] == source[sourceKeys[y]]) {
console.log(collection[t]);
} else {
console.log("value not found");
}
} else {
console.log("key not found");
}
}
}
}
Can anybody point out what I'm doing wrong?
I've also created a JSfiddle if you want to tinker.
I was also stuck on this for a good hour, when I stumbled upon a couple resources to assist.
I found that rather than the mess of nested for loops, I could use the built in looping methods to greatly simplify my code.
here is where I found my explanation:
https://github.com/Rafase282/My-FreeCodeCamp-Code/wiki/Bonfire-Where-art-thou
function where(collection, source) {
var arr = [];
var keys = Object.keys(source);
// Filter array and remove the ones that do not have the keys from source.
arr = collection.filter(function(obj) {
//Use the Array method every() instead of a for loop to check for every key from source.
return keys.every(function(key) {
// Check if the object has the property and the same value.
return obj.hasOwnProperty(key) && obj[key] === source[key];
});
});
return arr;
}
be more explicit in your declarations - helps to read the code easier:
var sourceKeys = Object.keys(source),
i = 0,
j = 0,
collectionLength = collection.length,
sourceKeysLength = sourceKeys.length;
while (i < collectionLength) {
j = 0;
while (j < sourceKeysLength) {
if (sourceKeys[j] in collection[i] && source[sourceKeys[j]] === collection[i][sourceKeys[j]]) {
console.log('found one!');
}
j++;
}
i++;
}
https://jsfiddle.net/fullcrimp/1cyy8z64/
Some insight here with clear understanding and less loops.
some new javascript function like some, filter, map are really handy to make code tidier as well.
function whatIsInAName(collection, source) {
// What's in a name?
var arr = [];
// Only change code below this line
collection.some(function(obj){
var sk = Object.keys(source); //keys of source object
var sv = Object.values(source); //values of source object
var temp = 0;
for(i=0;i<sk.length;i++){ // run until the number of source properties length is reached.
if(obj.hasOwnProperty(sk[i]) && obj[sk[i]] === sv[i]){ // if it has the same properties and value as parent object from collection
temp++; //temp value is increased to track if it has matched all the properties in an object
}
}
if(sk.length === temp){ //if the number of iteration has matched the temp value
arr.push(obj);
temp = 0; // make temp zero so as to count for the another object from collection
}
})
// Only change code above this line
return arr;
}
var collection = [{
first: "Romeo",
last: "Montague"
}, {
first: "Mercutio",
last: null
}, {
first: "Tybalt",
last: "Capulet"
}];
var source = {
last: "Capulet"
};
var collectionKeys = [];
for (var i = 0; i < collection.length; i++) {
collectionKeys.push(Object.keys(collection[i]));
}
var sourceKeys = Object.keys(source);
//for every key pair
for (var t = 0; t < collectionKeys.length; t++) {
//for every key in key pair
for (var x = 0; x < collectionKeys[t].length; x++) {
//for every key in search
for (var y = 0; y < sourceKeys.length; y++) {
//see if a key matches
if (sourceKeys[y] == collectionKeys[t][x]) {
if (collection[t][collectionKeys[t][x]] == source[sourceKeys[y]]) {
alert(collection[t].first+ " "+collection[t].last);
} else {
console.log("value not found");
}
} else {
console.log("key not found");
}
}
}
}
Change collection[collectionKeys[t][x]] to collection[t][collectionKeys[t][x]]..collection[collectionKeys[t][x]] gives undefined in console.
This is what I came to on the same problem.
function whereAreYou(collection, source) {
// What's in a name?
// Only change code below this line
var arr = [];
var validObject;
// check each object
for (var each_object in collection ){
validObject = true;
for (var key in source ){
if ( collection[each_object].hasOwnProperty(key)){
if ( collection[each_object][key] != source[key]){
// if no valid key
validObject = false;
}
} else {
// if no valid value
validObject = false;
}
}
// otherwise, give it a green light
if(validObject){
arr.push(collection[each_object]);
}
}
return arr;
}
function whatIsInAName(collection, source) {
const keyCount = Object.keys(source).length;
return collection.filter((item) => {
return Object.entries(item).reduce((acc, [key, value], _, arr) => {
if (keyCount > arr.length) {
acc = false;
} else if (keyCount === arr.length && !source[key]) {
acc = false;
} else if (source[key] && source[key] !== value) {
acc = false;
}
return acc;
}, true)
})
}
I would like a function that compares to arrays of javascript strings, and saving the values that didnt match in to a new array. At the moment im using a nested jquery foreach. But i think there are better ways than this?
$.each(imagesInUploadsFolder, function(i, outervalue){
$.each(imagesInDatabaseTable, function(i, innervalue){
if(outervalue == innervalue){
//match in both arrays...
}
});
});
How about this:
arr1.forEach( function ( elem ) {
if ( arr2.indexOf( elem ) > -1 ) {
// match...
}
});
where arr1 and arr2 are your two arrays...
(Btw, ES5 shim for IE8, of course...)
Here's a way using a JSON object and no jQuery, although the $.inArray() should work fine:
var imagesInUploadsFolder = [
'/path/to/img1.png',
'/path/to/img2.png',
'/path/to/img3.png'
];
var imagesInDatabaseTable = [
'/path/to/img1.jpg',
'/path/to/img2.png',
'/path/to/img4.png'
];
var database_json = JSON.stringify(imagesInDatabaseTable);
for (var i = 0; i < imagesInUploadsFolder.length; i++) {
console.log(imagesInUploadsFolder[i] + ' in ' + database_json);
if (database_json.indexOf(imagesInUploadsFolder[i]) > -1) {
console.log('In database: ' + imagesInUploadsFolder[i]);
} else {
console.log('Not in database: ' + imagesInUploadsFolder[i]);
}
}
http://jsfiddle.net/7nJPW/1/
EDIT
Actually, the JSON method isn't needed (?):
for (var i = 0; i < imagesInUploadsFolder.length; i++) {
console.log(imagesInUploadsFolder[i] + ' in ' + imagesInDatabaseTable);
if (imagesInDatabaseTable.indexOf(imagesInUploadsFolder[i]) > -1) {
console.log('In database: ' + imagesInUploadsFolder[i]);
} else {
console.log('Not in database: ' + imagesInUploadsFolder[i]);
}
}
http://jsfiddle.net/7nJPW/2/
Why not use foreach of pure javascript?
for (var i = 0; i < innervalue.length; i++) {
for (var j = 0; j < outervalue.length; j++){
if (innervalue[i] === outervalue[j])
// match
}
}
It is the most easier way i can think of right now :)
$.each(imagesInUploadsFolder, function(i, outervalue){
if($.inArray(imagesInDatabaseTable,outervalue)>-1){
//my operation
}
}
FYI: Actually inArray returns index of innermatch else -1. Just incase you need it.
Whether you use jQuery or for loops, straight-out comparison will be O(n2), since you'll need to compare each element of one array with every element of another array.
If the objects are comparable, you can sort the items using a suitable comparison function, then loop over the two arrays simultaneously, examining if one element is less than the other. If you're familiar with merge-sort, this is very similar to the merge step. Assuming the comparison function is O(1), sorting is O(nlog(n)), and the merge-like comparison loop is O(n), the total time complexity is O(nlog(n)), where "n" is the length of the larger array.
imagesInUploadsFolder.sort(imgCmp);
imagesInDatabaseTable.sort(imgCmp);
// diff will hold the difference of the arrays
var diff = [];
var i=0, j=0, cmp;
while (i < imagesInUploadsFolder.length && j < imagesInDatabaseTable.length) {
cmp = cmp(imagesInUploadsFolder[i], imagesInDatabaseTable[j]);
if (cmp < 0) {
// up[i] < db[j]
++i;
diff.append(imagesInUploadsFolder[i]);
} else if (cmp > 0) {
// up[i] > db[j]
++j;
diff.append(imagesInDatabaseTable[j]);
} else {
// up[i] == db[j]
++i; ++j;
}
}
// one of the arrays may still have items; if so, loop over it and add the items
if (i < imagesInUploadsFolder.length) {
for (; i < imagesInUploadsFolder.length; ++i) {
diff.append(imagesInUploadsFolder[i]);
}
} else if (j < imagesInDatabaseTable.length)) {
for (; i < imagesInDatabaseTable.length; ++i) {
diff.append(imagesInDatabaseTable[i]);
}
}
// diff now holds items that are in only one of the two arrays.
If you can define a suitable object ID function, you can create an ancillary data structure that holds a set of elements. If accessing object properties is O(f(n)) (for hashes, f ≈ 1; for balanced trees, f = log(n)), then this approach is O(n*f(n)), so it should have no worse complexity than the sort-and-compare approach. Untested and inefficient implementation:
function Set(from) {
this.elements = {};
this.size = 0;
if (from) {
for (var i=0; i < from.length) {
this.add(from[i]);
}
}
}
Set.prototype.each = function(f) {
var eltId;
foreach (eltId in this.elements) {
f(this.elements[eltId], eltId);
}
};
Set.prototype.clone = function() {
var clone = new Set();
this.each(function(obj, id) {
clone.add(obj);
});
return clone;
};
Set.prototype.contains = function(obj) {
return obj.uniqueId() in this.elements;
};
Set.prototype.add = function(obj) {
var objId = obj.uniqueId();
if (! (objId in this.elements)) {
++this.size;
this.elements[objId] = obj;
}
return this;
};
Set.prototype.remove = function(obj) {
var objId = obj.uniqueId();
if (objId in this.elements) {
--this.size;
delete this.elements[objId];
}
return this;
};
Set.prototype.union = function(other) {
other.each(function(elt, id) { this.add(elt); });
return this;
};
Set.prototype.sub = function(other) {
other.each(function (elt, id) {
this.remove(elt);
});
return this;
};
Set.prototype.diff = function(other) {
var mine = this.clone();
mine.sub(other);
var others = other.clone();
others.sub(this);
mine.union(others);
return mine;
};
Set.prototype.toArray = function(obj) {
var arr = [];
this.each(function(elt, id) {
arr.append(elt);
});
return arr;
};
var uploadsSet = new Set(imagesInUploadsFolder),
dbSet = new Set(imagesInDatabaseTable),
imagesInJustOne = uploadsSet.diff(dbSet);
If you want both the union and difference of the arrays, you can define a suitable method on Set to more efficiently calculate them instead of using Set.diff and Set.union separately.