Splicing objects from an array Javascript - javascript

I am trying to remove an object from an array if a particular value for a key matches a given string:
Example data:
array = [{_id: "abc", test: "123"},
{_id: "def", test: "123"},
{_id: "ghi", test: "123"}];
Here is my loop:
for (var i = 0; i < array.length; i++) {
var x = "123"
if (array[i].test == x) {
array.splice(i, 1)
}
}
This should return an empty array but it's leaving one object in the array (the last one) and I've got no clue why.

You must not increment i if you remove an element.
Suppose the array has two matching elements.
The first iteration, i = 0
i ==>> element 1
element 2
You then remove element 1, and increment i
element 2
i ==>>
There are many ways you can correct this. Here's an example that replaces your for loop with a while loop.
var i = 0;
while (i < array.length) {
var x = "123"
if (array[i].test == x) {
array.splice(i, 1)
} else {
++i;
}
}

Figured it out:
array = array.filter(function(array) {
var x = "123";
return array.test !== x;
})

array = [
{id: "abc", test: "123"},
{id: "def", test: "456"},
{id: "ghi", test: "789"}
];
let x = "123";
let i = array.findIndex(data => data.test === x);
if (i !== -1) {
array.splice(i, 1);
} else ...
console.log(array);

I have written a function that takes the index and event values from the html and processes in ts
app.component.html
<tr *ngFor="let container of containers; let i = index;"
[(ngModel)]="miningname"
ngDefaultControl>
<td>
<input *ngIf="addMore" type="text" class = "form-control"
placeholder="Key"
[(ngModel)]="container.key2Name"
(ngModelChange)="textKeyChangedMore($event,i)">
</td></tr>
app.component.ts
textKeyChangedMore(eventMore, indexMore) {
var valueLength = this.selectedAPIName.length
var i = indexMore+1;
if (this.selectedAPIName[i].value == undefined ) {
this.selectedAPIName.splice(indexMore + 1, 1, { key: eventMore, value: undefined });
}
else if (this.selectedAPIName[i].value !== undefined) {
this.selectedAPIName.splice(indexMore + 1, 1, { key: eventMore, value: this.selectedAPIName[i].value });
}
}

Related

How to Delete an item from an array with specific string matching

I want to be able to match a specific string (full match not partial match) and then delete that specific item from the array if it matches.
I have some code but it doesn't seem to be deleting the item from the array. I do wish for it to change the original array and not create a new array so I am not using filter.
How can I go about accomplishing this?
Current Code:
let recentSearches = [
{ name: "Chicago, IL" },
{ name: "Orlando, FL" },
{ name: "Dallas, TX" }
];
let stringToRemove = "Dallas, TX";
recentSearches.some(recent => {
if (recent.name === stringToRemove) {
const index = recentSearches.indexOf(stringToRemove);
if (index !== -1) { //Never goes into this if
recentSearches.splice(index, 1);
console.log(recentSearches);
}
}
});
console.log(recentSearches);
JS Fiddle: enter link description here
If you don't mind the output being a different array, use filter:
const filteredSearches = recentSearches.filter((recent) => recent.name !== stringToRemove);
If you need to modify the array in-place, you should visit the elements in reverse order (in case of multiple matches, which causes indices to shift) like so:
for (let i = recentSearches.length-1; i >= 0; i--) {
if (recentSearches[i].name === stringToRemove) {
recentSearches.splice(i, 1);
}
}
The problem with your code is you use recentSearches.indexOf, but recentSearches isn't an array of strings, so nothing matches. You could modify your code as follows, but it won't work correctly in case of multiple mathces:
recentSearches.forEach((recent, index) => {
if (recent.name === stringToRemove) {
recentSearches.splice(index, 1);
}
});
Alternatively, you could use findIndex (as suggested in other comments and answers) as follows:
let index;
while (0 <= (index = recentSearches.findIndex((recent) => recent.name === stringToRemove)) {
recentSearches.splice(index, 1);
}
indexOf() is for finding exact matches. Since your array contains objects, they'll never be equal to stringToRemove.
Use findIndex() to get the index of an array element using a function that an compare the name property.
There's also no need for using some().
let recentSearches = [{
name: "Chicago, IL"
},
{
name: "Orlando, FL"
},
{
name: "Dallas, TX"
}
];
let stringToRemove = "Dallas, TX";
const index = recentSearches.findIndex(({
name
}) => name == stringToRemove);
if (index !== -1) { //Never goes into this if
recentSearches.splice(index, 1);
}
console.log(recentSearches);
Another version of the findIndex, instead of using while, you could use for, a slight advantage here is that the index is then locally scoped inside the the for, were with a while loop you have the extra scope of the index, you could close the the scope of a let by doing { let index; while() {..}} but the for loop avoids that without using {}.
let recentSearches = [
{name: "Chicago, IL"},
{name: "Orlando, FL"},
{name: "Dallas, TX"}
];
let stringToRemove = "Dallas, TX";
for (let index; index = recentSearches.findIndex(
search => search.name === stringToRemove), index > -1;)
recentSearches.splice(index, 1);
console.log(recentSearches);
The JSON search is done wrongly.
I have added the perfect code to complete your requirement. Find all instances and delete them with a while loop. This will ensure duplicate search terms are also removed if any.
let recentSearches = [
{name: "Chicago, IL"},
{name: "Orlando, FL"},
{name: "Dallas, TX"}
];
let stringToRemove = "Dallas, TX";
while (recentSearches.findIndex(search => search.name === stringToRemove) > -1) {
const index = recentSearches.findIndex(search => search.name === stringToRemove);
recentSearches.splice(index, 1);
}
console.log(recentSearches);
You can use findindex.
Store it in a variable.
And use splice
You can use this code:
Array.prototype._arraycopy = function(src, srcPos, dest, destPos, length) {
while ((--length) >= 0) {
dest[destPos++] = src[srcPos++];
}
};
Array.prototype._fastRemove = function(es, i) {
let newSize;
if ((newSize = this.length - 1) > i)
this._arraycopy(es, i + 1, es, i, newSize - i);
es[this.length = newSize] = null;
this.length = newSize;
}
Array.prototype.__removeAt = function(index) {
// Objects.checkIndex(index, size);
const es = this;
const oldValue =es[index];
this._fastRemove(es, index);
return oldValue;
}
Array.prototype.__removeAtValue = function(o) {
const es = this;
const size = this.size;
let i = 0;
(function() {
if (o == null) {
for (; i < size; i++)
if (es[i] == null)
return true;
} else {
for (; i < size; i++)
if (Object.is(o, es[i]))
return true;
}
return false;
})()
this._fastRemove(es, i);
return true;
}
Array.prototype.remove = function(index) {
return this.__removeAt(index)
}
Array.prototype.removeObj = function(obj) {
return this.__removeAtValue(obj);
}
const arr = [1, 3, 4, 5, 10];
console.log(arr);
const rem = arr.remove(1)
console.log({ arr, rem });
const objs = [{ id: 1, name: "Hello" }, { id: 2, name: "Arrow" }, { id: 3, name: "Star" }]
console.log(objs);
const deleted = objs.removeObj({ id: 2, name: "Arrow" });
console.log({ objs, deleted })

Finding the index of an object within an array efficiently

I am trying to find the index of an object within an array. I know there is a way to do this with underscore.js but I am trying to find an efficient way without underscore.js. Here is what I have :
var arrayOfObjs = [{
"ob1": "test1"
}, {
"ob2": "test1"
}, {
"ob1": "test3"
}];
function FindIndex(key) {
var rx = /\{.*?\}/; // regex: finds string that starts with { and ends with }
var arr = []; // creates new array
var str = JSON.stringify(arrayOfObjs); // turns array of objects into a string
for (i = 0; i < arrayOfObjs.length; i++) { // loops through array of objects
arr.push(str.match(rx)[0]); // pushes matched string into new array
str = str.replace(rx, ''); // removes matched string from str
}
var Index = arr.indexOf(JSON.stringify(key)); // stringfy key and finds index of key in the new array
alert(Index);
}
FindIndex({"ob2": "test1"});
JSFIDDLE
This works but I am afraid it isn't very efficient. Any alternatives?
Here's one way to do it, somewhat reliably and a little more efficiently, using some() and stopping as soon as the objects don't match etc.
var arrayOfObjs = [{
"ob1": "test1"
}, {
"ob2": "test1"
}, {
"ob1": "test3"
}];
function FindIndex(key) {
var index = -1;
arrayOfObjs.some(function(item, i) {
var result = Object.keys(key).some(function(oKey) {
return (oKey in item && item[oKey] === key[oKey]);
});
if (result) index = i;
return result;
});
return index;
}
var index = FindIndex({"ob2": "test1"});
document.body.innerHTML = "'{\"ob2\": \"test1\"}' is at index : " + index;
A hash table with an example of access.
var arrayOfObjs = [{ "obj1": "test1" }, { "obj2": "test1" }, { "obj1": "test3" }],
hash = {};
arrayOfObjs.forEach(function (a, i) {
Object.keys(a).forEach(function (k) {
hash[k] = hash[k] || {};
hash[k][a[k]] = i;
});
});
document.write('<pre>' + JSON.stringify(hash['obj2']['test1'], 0, 4) + '</pre>');
document.write('<pre>' + JSON.stringify(hash, 0, 4) + '</pre>');
One way of doing this would be to use every to see if each key in the "filter" has a matching, correct value in an object. every ensures that the loop stops as soon as it finds a mismatched or missing value.
function log(msg) {
document.querySelector('pre').innerHTML += msg + '\n';
}
var arr = [
{
a: 1
},
{
b: 2
},
{
c: 3,
d: 4
},
{
a: 1 // Will never reach this since it finds the first occurrence
}
];
function getIndex(filter) {
var keys = Object.keys(filter);
for (var i = 0, len = arr.length; i < len; i++) {
var obj = arr[i];
var match = keys.every(function(key) {
return filter[key] === obj[key];
});
if (match) {
return i;
}
}
return -1;
}
log(getIndex({ a: 1 }));
log(getIndex({ b: 2 }));
log(getIndex({ c: 3 }));
log(getIndex({ c: 3, d: 4 }));
log(getIndex({ e: 5 })); // Doesn't exist, won't find it
<pre></pre>
For an alternative to your customly built approach, lodash's findIndex method does exactly this for you:
var arrayOfObjs = [{
"ob1": "test1"
}, {
"ob2": "test1"
}, {
"ob1": "test3"
}];
_.findIndex(arrayOfObjs, {"ob2": "test1"}); // => 1
Since testing equality on two different objects will always return false you could first test keys and then values ,
using reduce :
var arrayOfObjs = [{
"ob1": "test1"
}, {
"ob2": "test1" , k2:2
}, {
"ob1": "test3"
}];
function getI( obj, arr){
const checkK= Object.keys(obj);
return arr.reduce((ac,x,i) => {
if ( checkK.every(z => x[z] && obj[z] === x[z]) )
ac.push(i);
return ac;
},[])
}
document.write( 'result is :'+ getI({ob2:'test1', k2:2},arrayOfObjs))
findIndex won't work in old browsers, but was designed for this specific purpose.
var arrayOfObjs = [{
"ob1": "test1"
}, {
"ob2": "test1"
}, {
"ob1": "test3"
}];
function FindIndex(key) {
return arrayOfObjs.findIndex(
obj => Object.keys(key).every(name => key[name] === obj[name])
);
}
alert(FindIndex({"ob2": "test1"})); // 1

merging two array of objects with custom compare function with javascript

I have two array of objects like:
var A = [{title:"name1",count:5},{title:"name2",count:1},{title:"name3",count:3}];
and:
var B = [{title:"name2",count:7},{title:"name3",count:2},{title:"name4",count:3},{title:"name5",count:8}];
I need to merge this two array in one array and sum the "count" values in returned array when the "title" properties is same:
the last answer must be:
[{title:"name1",count:5},{title:"name2",count:8},{title:"name3",count:5},{title:"name4",count:3},{title:"name5",count:8}]
how can i do this???
You can use Array#forEach and Array#some to achieve a result
var M = A.concat(B)
var C = [];
M.forEach(function(a) {
var index;
if (C.some(function(c, i) { index = i; return a.title == c.title; })) {
C[index].count += a.count;
} else {
C.push(a);
}
});
console.log(C); // as you expect
Solution with Array.concat and Array.map functions:
var merged = A.concat(B), titles = [], result = [];
merged.map(function(obj){
if (titles.indexOf(obj.title) === -1) {
titles.push(obj.title);
result.push(obj);
} else {
result[titles.indexOf(obj.title)]['count'] += obj['count'];
}
});
console.log(result); // will output the expected array of objects
It can be done like this https://jsfiddle.net/menm9xeo/
var noMatch;
var A = [{title:"name1",count:5},{title:"name2",count:1},{title:"name3",count:3}];
var B = [{title:"name2",count:7},{title:"name3",count:2},{title:"name4",count:3},{title:"name5",count:8}];
//for each A, loop through B's. If a match is found combine the Counts in A.
for(var i=0;i<A.length;i++){
for(var j=0;j<B.length;j++){
if(A[i].title == B[j].title){
A[i].count += B[j].count;
}
}
}
//find all B's that were not combined with A in the previous step, and push them into A.
for(var i=0;i<B.length;i++){
noMatch = true;
for(var j=0;j<A.length;j++){
if(B[i].title == A[j].title){
B[i].count += A[j].count;
noMatch = false;
}
}
if(noMatch){A.push(B[i]);}
}
Heres a simple 3 line answer (minus the A/B vars); utilizes the fact that objects must have unique keys
var A = [{title:"name1",count:5},{title:"name2",count:1},{title:"name3",count:3}];
var B = [{title:"name2",count:7},{title:"name3",count:2},{title:"name4",count:3},{title:"name5",count:8}];
var o = {};
A.concat(B).forEach(function(a){o[a.title] = o.hasOwnProperty(a.title)? o[a.title]+a.count: a.count});
var AB = Object.keys(o).map(function(j){ return {title:j,count:o[j]} });
This proposal is merging and counting with a temporary object and Array#forEach()
The forEach() method executes a provided function once per array element.
var arrayA = [{ title: "name1", count: 5 }, { title: "name2", count: 1 }, { title: "name3", count: 3 }],
arrayB = [{ title: "name2", count: 7 }, { title: "name3", count: 2 }, { title: "name4", count: 3 }, { title: "name5", count: 8 }],
result = function (array) {
var o = {}, r = [];
array.forEach(function (a) {
if (!(a.title in o)) {
o[a.title] = { title: a.title, count: 0 };
r.push(o[a.title]);
}
o[a.title].count += a.count;
});
return r;
}(arrayA.concat(arrayB));
document.write('<pre>' + JSON.stringify(result, 0, 4) + '</pre>');
Using lodash ._concat function :
var result = _.concat(A, B);
Fiddle

Remove JSON entry by value [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Delete from array in javascript
I have the following JSON object:
[id:84,id:92,id:123,id:2353]
How would I go about removing the item which the value is "123" using javascript?
or if I formatted the json as
[84, 92, 123, 2353]
How would it be removed in this case?
Assume you have this:
var items = [{ id: 84 }, { id: 92 }, { id: 123 }, { id: 2353 }];
var filtered = items.filter(function(item) {
return item.id !== 123;
});
//filtered => [{ id: 84 }, { id: 92 }, { id: 2353 }]
Supposing you actually have an object from a json in the json variable
for (key in json) {
if (json.hasOwnProperty(key) && json[key] == 123) {
delete json[key];
}
}
Shorter alternative would be:
var newArr = [{id:84}, {id:92}, {id:123}, {id:2353}].filter(function(a) {
return a.id != 123;
});
If you have this:
var arr = [{id:84}, {id:92}, {id:123}, {id:2353}]
To remove the item with value 123, you can do:
for(var i = 0; i < arr.length; i++) {
if(arr[i].id == 123) {
arr.splice(i, 1);
break;
}
}
function removeClass(obj, cls) {
var classes = obj.className.split(' ');
for(i=0; i<classes.length; i++) {
if (classes[i] == cls) {
classes.splice(i, 1);
i--; // (*)
}
}
obj.className = classes.join(' ');
}
var obj = { className: 'open menu menu' }
removeClass(obj, 'menu')
alert(obj.className)
You can use splice function, like this:
var data = [{id:84}, {id:92}, {id:123}, {id:2353}];
function remove(){
for(var i = 0, max = data.length; i < max; i++) {
var a = data[i];
if(a.id === 123) {
data.splice(i, 1);
break;
}
}
}
remove();
Seems like you want to avoid a loop. Assuming it's available, you can use .filter:
[{id:84},{id:92},{id:123},{id:2353}]
.filter(function (elem) { return elem.id !== 123; });
This technically does do a loop, but at least you don't have to look at it.
Assuming your "json" is really an array, like [84, 92, 123, 2353]:
var myString = "[84, 92, 123, 2353]";
var myArray = JSON.parse(myString);
var index = myArray.indexOf(123); // whatever value you are looking for
myArray.splice(index, 1);
http://jsfiddle.net/7vkK6/
Assuming I'm understanding your question and comments correctly you can do something like this:
var old_array = [{id: 84},...];
var new_array = [];
for(var i = 0, len = old_array.length; i++) {
if (old_array[i].id != 123) new_array.push(old_array[i]);
}
What you have currently is not JSON so I'll give you some different options.
If you have an Array arr = [84,92,123,2353] then
arr = arr.filter(function (x) {return x !== 123;}); // all occurrences
// OR
arr.splice(arr.indexOf(123), 1); // first occurrence only
If you have an Object obj = {"84": a, "92": b, "123": c, "2353": d}, a to d some expressions, then
delete obj['123']; // obj now {"84": a, "92": b, "2353": d}
1) JSON is a string, not an array or an object.
var json = "[1,2,3]";
2) Valid JSON NEEDS to be valid JS
var myJSObj = { 1,2,3 }, // broken
myJSArr = [ name : 1, name2 : 2 ]; // broken
3) If you have a JS Array, you can remove an element by using [].splice
var arr = [ 1, 2, 3, 4 ],
i = 0, l = arr.length,
test = 4;
for (; i < l; i += 1) {
if (arr[i] === test) { arr.splice(i, 1); } // remove 1 starting at i
}
4) If you have an object with named keys, you can use delete
var obj = { val : 1 };
delete obj.val;

Need an algorithm to manipulate array structure in javascript

In javascript, here is my start array:
[{
name: 'aaa',
value: 1
},
{
name: 'bbb',
value: 0
},
{
name: 'bbb',
value: 1
}]
I want to transform it into this array as result:
[{
name: 'aaa',
value: 1
},
{
name: 'bbb',
value: [0, 1]
}]
I need a good and simple algorithm to do this
How about:
var array = [{
name: 'aaa',
value: 1
},
{
name: 'bbb',
value: 0
},
{
name: 'bbb',
value: 1
}];
var map = {};
for(var i = 0; i < array.length; i++) {
var name = array[i].name;
if (map[name] === undefined) {
map[name] = [];
}
map[name].push(array[i].value);
}
var result = [];
for(var key in map) {
var value = map[key];
result.push({
name: key,
value: value.length === 1 ? value[0] : value
});
}
Easiest way is to create a map to keep track of which names are used. Then convert this map back to an array of objects.
If you want to use Arrays for value then change it to:
result.push({
name: key,
value: value
});
here's pseudocode for simplest implementation
hash = {}
for(pair in array) {
hash[pair.name] ||= []
hash[pair.name] << pair.value
}
result = []
for(k, v in hash) {
result << {name: k, value: v}
}
This function does the trick
function consolidate(var arrayOfObjects)
{
// create a dictionary of values first
var dict = {};
for(var i = 0; i < arrayOfObjects.length; i++)
{
var n = arrayOfObjects[i].name;
if (!dict[n])
{
dict[n] = [];
}
dict[n].push(arrayOfObjects[i].value);
}
// convert dictionary to array again
var result = [];
for(var key in dict)
{
result.push({
name: key,
value: dict[key].length == 1 ? dict[key][0] : dict[key]
});
}
return result;
}
An alternative solution:
function convert(arr) {
var res = [];
var map = {};
for (var i=0;i<arr.length;i++) {
var arrObj = arr[i];
var oldObj = map[arrObj.name];
if (oldObj == undefined) {
oldObj = {name:arrObj.name, value:arrObj.value};
map[arrObj.name] = oldObj;
res.push(oldObj);
} else {
if( typeof oldObj.value === 'number' ) {
oldObj.value = [oldObj.value];
}
oldObj.value.push(arrObj.value);
}
}
return res;
}
In theory it should work a bit faster and use less memory. Basically it creates a result array and a map which is an index for the same array (no duplicate objects). So it fills the result in one iteration instead of two and does not need to convert map to array (which saves several CPU cycles :P ).
Added:
Here is a variation of that function in case value: [1] is acceptable:
function convert(arr) {
var res = [];
var map = {};
for (var i=0;i<arr.length;i++) {
var arrObj = arr[i];
var oldObj = map[arrObj.name];
if (oldObj == undefined) {
oldObj = {name:arrObj.name, value:[arrObj.value]};
map[arrObj.name] = oldObj;
res.push(oldObj);
} else {
oldObj.value.push(arrObj.value);
}
}
return res;
}

Categories

Resources