I have two arrays of objects and I want to compare the objects of the first array to the ones of the second array. If they match, I use the splice to remove the object from the second array.
I have the following code
existing.forEach((existingitem, existingindex, existingfeatures) => {
(newdatafeat.features).forEach((newitem, newindex, newfeatures) => {
console.log('existing index-name --- new index-name', existingindex ,' - ',existingitem.values_.name,' - ',newindex,' - ',newitem.properties.name,'-----');
if (existingitem.values_.id == newitem.properties.id && existingitem.values_.cat == newitem.properties.cat){
console.log(' index to remove - ', newindex); (newdatafeat.features).splice(newindex,1);
}
})
});
So, If existing is
var existing= [
{ info: true, values_:{id:1, cat:true, name : "John"} },
{ info : true, values_:{id:2, cat:false, name : "Alice"} }
];
and newdatafeat.features is
var newdatafeat= {
status:scanned,
features : [ { info: true, properties:{id:1, cat:true, name : "Mike"} },
{ info : false, properties:{id:22, cat:false,name : "Jenny"} } ]
};
Then, Mike from newdatafeat.features should be removed.
The error is that every item of the newdatafeat.features array with index 0 is not removed. In the loop, I can see index to remove - 0, but Mike is never removed. I know, because if I console.log the newdatafeat.features after the loops, Mike is there
This is inside an angular6 code.
What am I missing here?
Thanks
I had to clean up some code, but it looks like yours is working fine. It identified one element to be removed, called slice and it was gone.
var existing = [{
info: true,
values_: {
id: 1,
cat: true,
name: "John"
}
},
{
info: true,
values_: {
id: 2,
cat: false,
name: "Alice"
}
}
];
var newdata = {
status: "scanned",
features: [
{
info: true,
properties: {
id: 1,
cat: true,
name: "Mike"
}
},
{
info: false,
properties: {
id: 22,
cat: false,
name: "Jenny"
}
}
]
};
existing.forEach(
(existingitem, existingindex, existingfeatures) => {
(newdata.features).forEach((newitem, newindex, newfeatures) => {
console.log('existing index-name --- new index-name', existingindex, ' - ', existingitem.values_.name, ' - ', newindex, ' - ', newitem.properties.name, '-----');
if (existingitem.values_.id == newitem.properties.id && existingitem.values_.cat == newitem.properties.cat) {
console.log(' index to remove - ', newindex);
(newdata.features).splice(newindex, 1);
}
})
});
console.log(newdata.features);
The main problem here is that you are iterating an array in a loop, but you are removing items from that array in the same loop. So the index will often be off, and the wrong element or no element will be removed. It can be hard to reproduce with simple examples, but here ya go:
function removeVowels(letters) {
letters.forEach((element, index, arr) => {
if ('aeiou'.indexOf(element) > -1) {
arr.splice(index, 1);
}
});
}
var myArray = ['a','b','c','d','e'];
removeVowels(myArray);
console.log(myArray);
// works great!
var myArray = ['a','e','c','d','b'];
removeVowels(myArray);
console.log(myArray);
// wtf!
A simple way to approach this problem is to handle the looping manually, and change the index manually if you DO remove an element.
function removeVowels(letters) {
for (var i=0; i < letters.length; i++) {
if ('aeiou'.indexOf(letters[i]) > -1) {
letters.splice(i, 1);
i--;
}
}
}
var myArray = ['a','b','c','d','e'];
removeVowels(myArray);
console.log(myArray);
// works great!
var myArray = ['a','e','c','d','b'];
removeVowels(myArray);
console.log(myArray);
// hurray!
Related
I have an array of objects, and according to a property inserted in one of them i would like to mark or select all the objects previous to that object container of the specific property
My array is in this way:
const arrX= [
{ status: '1' },
{ status: '2'},
{ status: '3', imHere: true },
{ status: '4' },
];
Then due to the property imHere on arrX[2], the positions arrX[0] and arrX[1] should be modified.
My expected result would be :
const arrX= [
{ status: '1',wasHere:true },
{ status: '2',wasHere:true},
{ status: '3', imHere: true },
{ status: '4' },
];
I know that the map method would be quite useful in this case, but canĀ“t find the way to check from index of object containing imHere backwards the former positions
One approach is to use .findIndex() and .map():
const arrX= [{ status: '1' }, { status: '2'}, { status: '3', imHere: true }, { status: '4'}];
const imHereIndex = arrX.findIndex(({imHere}) => imHere === true);
const result = arrX.map((val, index) => index < imHereIndex
? { ...val, wasHere: true }
: val
);
console.log(result);
Even if #Kinglish answer works like a charm I want to share another way to achieve your goal. This road is surely longer than Kinglish ones, never then less is a good alternative.
{ status: '4' },
];
function findProperty(arr) {
const hasProperty = arr.findIndex(el => Object.keys(el).includes('imHere'))
const addNewProperty = arr.map((el,i) => (i < hasProperty) ? {...el, wasHere: true} : el)
return addNewProperty
}
const updatedArray = findProperty(arrX)
console.log(updatedArray)
Here's one method for it using Array#reduce and a boolean to track whether we've encountered inHere
const arrX = [
{status: '1'},
{status: '2'},
{status: '3',imHere: true},
{status: '4'},
];
let found = false,
updated = arrX.reduce((b, a) => {
found = found || (a.hasOwnProperty('imHere') && a.imHere === true)
if (!found) a.wasHere = true;
return b.concat(a);
}, [])
console.log(updated)
A simple loop - breaking out of it when one of the objects contains imHere, otherwise adding in a wasHere property.
function update(arr) {
for (let i = 0; i < arr.length; i++) {
if (!arr[i].imHere) {
arr[i].wasHere = true;
} else {
break;
}
}
return arr;
}
const arr = [
{ status: '1' },
{ status: '2' },
{ status: '3', imHere: true },
{ status: '4' },
];
console.log(update(arr));
For example, has the following data:
let example = {
content: [
...
{ // index = 3
id: "b3bbb2a0-3345-47a6-b4f9-51f22b875f22",
data: {
value: "hello",
content: [
...
{ // index = 0
id: "65b1e224-4eae-4a6d-8d00-c1caa9c7ed2a",
data: {
value: "world",
content: [
...
{ // index = 1
id: "263a4961-efa7-4639-8a57-b20b23a7cc9d",
data: {
value: "test",
content: [
// Nesting unknown.
]
}
}
]
}
}
]
}
}
]
}
And for example an array with indexes leading to the required element(but can be any other):
const ids = [3, 0, 1]
How can you work with an element having this data?
For example, need to change "value" in the element at the specified path in "ids".
You could take an array of indices and get the item of the property content by calling the function again for each missing index.
const
getElement = ({ content }, [index, ...indices]) => indices.length
? getElement(content[index], indices)
: content[index];
If needed, you could add a guard for a missing index and exit early.
You can just recursively loop over your element and change the value, if you got to the last element.
I have written a small example, all you would have to do, is to extend the method for your own data structure (el.data.content):
const el = [[1,2], [3,4], [5,6]];
const changeEl = (el, indexArr, val) => {
if(indexArr.length == 1) {
el[indexArr[0]] = val;
} else {
changeEl(el[indexArr.shift()], indexArr, val);
}
}
changeEl(el, [1, 0], 99);
console.log(el);
I have an array of twelve items :
var arrToIterate = [];
arrToIterate = //Data from service to populate;
Each item in the array has two fields : Name and title.
If suppose the array has Names as :
Scenario 1:
[0]: Name: Iron
[1]: Name: Steel
[2]: Name: ContainsIron
[3]: Name: ContainsSteel
[4]: Name : Manganese
[5]: Name: Magnesium
I need the ouput as :
[0]: Name: Iron
[1]: Name: Steel
[2]: Name : Manganese
[3]: Name: Magnesium
Scenario 2:
If suppose the array has Names as :
[0]: Name: Iron
[1]: Name: Steel
[2]: Name : Manganese
[3]: Name: Magnesium
I need the output as:
[0]: Name: Manganese
[1]: Name: Magnesium
I am trying to do in this way :
$.each(arrToIterate, function (element, index, arr) {
if (element.Name == "ContainsIron" || element.Name == "ContainsSteel") {
arr.splice(index, 1);
}
});
But I am not getting the handle the second scenario . How do I achieve array manipulation for both the scenarios?
Edit :
If the array contains "ContainsIron" , then ContainsIron needs to be removed ,similarly if it contains ContainsSteel , then ContainsSteel needs to be removed.
Else
If array doesnt contain ContainsSteel , then Steel needs to be removed .
If array doesnt contain ContainsIron, then Iron needs to be removed .
Here's a simple version. First, you'll want to make a remove function which removes something if it's in there, and a function which removes a named metal:
function removeIfExists(arr,name){
// get the index of the entry:
for(var i in arr){
if(arr[i].Name==name){
// Got it! Rip it out:
arr.splice(i,1);
return true;
}
}
// Not in the array at all.
return false;
}
// Removes a metal from the given array
function removeMetal(arr,name){
// Try removing e.g. ContainsIron:
if(!removeIfExists(arr,"Contains"+name)){
// ContainsIron/ ContainsSteel wasn't in there. Try removing 'Iron'/ 'Steel':
removeIfExists(arr,name);
}
}
That leaves the usage as just:
removeMetal(arrToIterate,"Iron");
removeMetal(arrToIterate,"Steel");
Here it is as a fiddle
You can just use vanilla Javascript to do this, using the filter function:
var arrToIterate = [{
name: 'Iron'
}, {
name: 'Iron'
}, {
name: 'Iron'
}, {
name: 'Manganese'
}, {
name: 'Iron'
}, {
name: 'Iron'
}, {
name: 'Iron'
}, {
name: 'ContainsSteel'
}];
filterArray(arrToIterate);
function filterArray(arrToIterate) {
var containsSteel = false,
containsIron = false,
filteredArray;
arrToIterate.forEach(function(item) {
(item.name === 'ContainsSteel') ? containsSteel = true: null;
(item.name === 'ContainsIron') ? containsIron = true: null;
});
console.log("Original Array");
console.log(arrToIterate);
console.log("ContainsSteel " + containsSteel);
console.log("ContainsIron " + containsIron);
if (containsSteel) {
filteredArray = arrToIterate.filter(function(item) {
return !(item.name === 'ContainsSteel');
});
}
if (containsIron) {
filteredArray = filteredArray.filter(function(item) {
return !(item.name === 'ContainsIron');
});
}
if (!(containsIron)) {
filteredArray = filteredArray.filter(function(item) {
return !(item.name === 'iron');
})
}
if (!(containsSteel)) {
filteredArray = filteredArray.filter(function(item) {
return !(item.name === 'Steel');
})
}
console.log("Filtered array ");
console.log(filteredArray);
return filteredArray;
};
Because of the lack of consistency of the "Contain" rules (Manganese for example, doesn't have them), we need to define a hash of what won't be displayed if there isn't a contain rule for it.
Afterwards we can scan the array for for "Contain" rules update the hash, and then just filter array accordingly.
var arrToIterate = [
{ Name: 'Iron' },
{ Name: 'Steel' },
{ Name: 'ContainsIron' },
{ Name: 'Manganese' },
{ Name: 'Magnesium' }
];
var result = arrToIterate.filter(function(metal) {
return metal.Name in this ? this[metal.Name] : true; // if there's an entry in the hash (this) use it. If not keep the item.
}, arrToIterate.reduce(function(hash, metal) { // create the hash
if(metal.Name.indexOf('Contains') !== -1) { // if there's a contain rule
hash[metal.Name] = false; // remove the rule
hash[metal.Name.replace('Contains', '')] = true; // show the metal
}
return hash;
}, { Iron: false, Steel: false })) // the baseline is false for every metal with a Contain rule
console.log(result);
If you just want to filter the array you can use filter:
var array = [
{ name: 'Iron'},
{ name: 'Steel'},
{ name: 'Manganese'},
{ name: 'Magnesium'}
];
var filtered = array.filter(function(item) {
return item.name === 'Iron';
})
Not sure what your selection criteria is, but you just need to define these in the body of the filter callback.
Edit
I see you updated your criteria - so you would just have to amend the filter callback accordingly as I think other responders have now indicated.
I build an array like this
result.push({ id: id, reference: sometext });
Now I want to sort this array by reference, which has some text.
I tried this:
result.sort(function(a,b) {
return result[a]-result[b];
});
This
[ { id: 1, reference: 'banana' },
{ id: 2, reference: 'apple' } ]
should get
[ { id: 2, reference: 'apple' },
{ id: 1, reference: 'banana' } ]
Try this.
result.sort(function(a,b) {
// Compare reference
if(a.reference < b.reference) {
// a's reference is lesser than the one in b
return -1;
} else if (a.reference == b.reference) {
// Both reference params are equal
return 0;
} else {
// a's reference is greater than that of b
return 1
}
});
This will return a sorted version of the results array.
Do it like this instead:
result.sort(function(a,b) {
return a.reference < b.reference ? -1 : 1;
});
I have the following 2 arrays here:
> chNameArr
[ 'chanel1',
'chanel2',
'chanel3',
'chanel4',
'chanel5',
'chanel6',
'chanel7' ]
and here:
> a
[ 'channelName'
'status',
'connections',
'socketIds',
'lastRun',
'numberOfRuns',
'timeout' ]
what I am trying to achieve is the following objects per channel in an array where channelName from a get the value from chNameArr but the rest of 'a' gets an empty string
file=[{"channelName":"chanel1","status":"", "connections":"", "socketIds":"", "lastRun":"", "numberOfRuns":"", "timeout":""},
.
.
.
{"channelName":"chanel7","status":"", "connections":"", "socketIds":"", "lastRun":"", "numberOfRuns":"", "timeout":""}]
this is my attempt
> chNameArr.map(function(d){return {channelName:d}})
[ { channelName: 'chanel1' },
{ channelName: 'chanel2' },
{ channelName: 'chanel3' },
{ channelName: 'chanel4' },
{ channelName: 'chanel5' },
{ channelName: 'chanel6' },
{ channelName: 'chanel7' } ]
chNameArr.map(function(d) {
result = {};
result[a[0]] = d;
for (var i=1; i<a.length; i++) {
result[a[i]] = "";
}
return result;
})
There is no one-liner to solve this problem in general, although if you don't actually need to use the array a you could manually construct {channelName:d, status: "", ...} in your original map.