sort array of objects by object field - javascript

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;
});

Related

Save last elements from deep nested object to a non global array

I already found out, how to get the last elements of a deep nested object.
See here for working example: How to get the last children in a deeply nested array with objects
Now I dont want to log the names like in the example with console.log(subObj.name), instead I want to save them in an array, which shouldnt be global. I just want a function return this array.
Is this somehow possible without declaring a global array for this ?
This is my code:
function childrenNames (obj) {
var lastChildren = [];
obj.forEach((subObj) => {
if (subObj.hasOwnProperty('specification') && subObj.specification instanceof Array && subObj.specification.length > 0) {
childrenNames(subObj.specification);
} else {
if (subObj.hasOwnProperty('name')) {
lastChildren.push(subObj.name)
}
}
})
console.log(lastChildren);
return lastChildren
}
But its just returning 4 different arrays instead of 1 containing all last children.
I am not sure whether this is a valid answer and I do not understand it but I am leaving it for now because it does appear, superficially at least, to answer the question. It does not require a global array to be declared as far as I can tell?
var obj = [
{
name: 'something',
children: [
{
name: 'something',
children: [
{
name: 'no child'
},
{
name: 'something empty',
children: [ ]
}
]
}
]
},
{
name: 'something',
children: [
{
name: 'something',
children: [
{
name: 'no child'
}
]
}
]
},
{
name: "children isn't an array",
children: 42
}
]
function childrenNames (obj) {
var lastChildren = [];
obj.forEach((subObj) => {
if (subObj.hasOwnProperty('specification') && subObj.specification instanceof Array && subObj.specification.length > 0) {
childrenNames(subObj.specification);
} else {
if (subObj.hasOwnProperty('name')) {
lastChildren.push(subObj.name)
}
}
})
// console.log(lastChildren);
return lastChildren
}
const res = childrenNames(obj);
console.log('res', res);

splice is not removing the first index from array of objects

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!

Looping through (possibly infinitely) repeating object structure

I have a problem I can't get my head around. If I am looking for an object with a certain ID in a possibly infinite data structure, how can I loop through it until I find the object I need and return that object?
If this is what my data looks like, how can I get the object with id === 3 ?
{
id: 0,
categories: [
{
id: 1,
categories: [
{
id: 2,
categories: [ ... ]
},
{
id: 3,
categories: [ ... ]
},
{
id: 4,
categories: [ ... ]
},
]
}
]
}
I tried the following:
findCategory = (categoryID, notesCategory) => {
if (notesCategory.id === categoryID) {
return notesCategory;
}
for (let i = 0; i < notesCategory.categories.length; i += 1) {
return findCategory(categoryID, notesCategory.categories[i]);
}
return null;
};
But that doesn't get ever get to id === 3. It checks the object with id: 2 and then returns null. It never gets to the object with id: 3.
Here is a JSbin: https://jsbin.com/roloqedeya/1/edit?js,console
Here is the case. when you go in to the first iteration of 'for' loop, because of the return call, the execution is go out from the function. you can check it by using an console.log to print the current object in the begin of your function.
try this
function find(obj, id) {
if(obj.id === id) {
console.log(obj) // just for testing. you can remove this line
return obj
} else {
for(var i = 0; i < obj.categories.length; i++) {
var res = find(obj.categories[i], id);
if(res) return res;
}
}
}
hope this will help you. thanks
You need to store the intermediate result and return only of the object is found.
function findCategory(object, id) {
var temp;
if (object.id === id) {
return object;
}
object.categories.some(o => temp = findCategory(o, id));
return temp;
}
var data = { id: 0, categories: [{ id: 1, categories: [{ id: 2, categories: [] }, { id: 3, categories: [] }, { id: 4, categories: [] }] }] }
result = findCategory(data, 3);
console.log(result);

how to use .include() method to check the value which is in a json inside array

I want to compare the value of a particular key in my JSON array with new value to check whether the value exists or not.
For example, I have an array:
[
{ name: abc, num: 121212 },
{ name: bcd, num: 21212 },
{ name: def, num: 111222 }
]
Now a new value comes which I want to check. Does that name already exist? If it does, then I only want to update the number and if not then I want to push the object in the array.
Here is my code:
if ((Dnum.num).includes(number)) {
console.log("inside if");
console.log(Dnum.indexOf(number));
} else {
Dnum.push({num:number,
lat:lat,
lng:lng,
name:name
});
}
Well, your problem (if I understand correctly) is that you want to use includes() but what you actually want to accomplish doesn't correspond to what the method does. You want to find if there's an object with a certain name in your array already, not if it contains a known element. Something like this:
var data = [{name: 'abc', num: 121212}, {name: 'bcd', num: 21212}, {name: 'def', num: 111222}];
function addOrUpdate(newElement, data) {
var i;
for (i = 0; i < data.length; i++) {
if (data[i].name == newElement.name) {
data[i] = newElement;
return;
}
}
data.push(newElement);
}
addOrUpdate({name: 'bcd', num: 131313}, data);
console.log(data);
addOrUpdate({name: 'new', num: 131313}, data);
console.log(data);
Problem:
Actually .includes() and .indexOf() methods won't work with objects, they should be used with an array of strings or Numbers as they use strict equality to compare the elements and objects can't be compared this way, so you need to implement this logic by yourself.
Solution:
You need to check if an object matching the searched name already exists in the array, update the num value of this object, otherwise if no object matches the searched name, push the new object to the array:
if (arr.some(function(obj) {
return obj.name === searchedVal.name;
})) {
arr.forEach(function(el, index) {
if (el.name === searchedVal.name) {
el.num += searchedVal.num;
found = true;
}
});
} else {
arr.push(searchedVal);
}
Demo:
var arr = [{
name: "abc",
num: 121212
}, {
name: "bcd",
num: 21212
}, {
name: "def",
num: 111222
}];
var searchedVal = {
name: "abc",
num: 5
};
if (arr.some(function(obj) {
return obj.name === searchedVal.name;
})) {
arr.forEach(function(el, index) {
if (el.name === searchedVal.name) {
el.num += searchedVal.num;
found = true;
}
});
} else {
arr.push(searchedVal);
}
console.log(arr);
If you don't want to use .some() method, you can do it this way:
var searchedVal = {
name: "abc",
num: 5
};
var found = false;
arr.forEach(function(el, index) {
if (el.name === searchedVal.name) {
el.num+= searchedVal.num;
found = true;
}
});
if (!found) {
arr.push(searchedVal);
}
Use Array.prototype.find():
var res = Dnum.find(function (item) {
return item.num === number;
});
if (res) {
console.log("inside if");
console.log(res);
res.num = number;
} else {
Dnum.push({
num:number,
lat:lat,
lng:lng,
name:name
});
}

How to turn the following recursive function into a pure function?

The following function appends an object into a nested array (by searching for it recursively):
function appendDeep (arr, obj, newObj) {
if (arr.indexOf(obj) !== -1) {
arr.splice(arr.indexOf(obj) + 1, 0, newObj)
} else {
arr.map(item => {
if (item.children) spliceDeep(item.children, obj)
})
}
}
Example:
const colors = {
children: [
{
name: 'white',
},
{
name: 'yellow',
children: [
{
name: 'black'
}
]
}
]
}
const color = {
name: 'black'
}
const newColor = {
name: 'brown'
}
appendDeep(colors.children, color, newColor)
Result:
children: [
[
{
name: 'white',
},
{
name: 'yellow',
children: [
{
name: 'black'
},
{
name: 'brown'
}
]
}
]
]
As you can see appendDeep returns a side-effect; it modifies arr. So I decided to return the array instead (so the function would become pure):
function findDeep (arr, obj) {
if (arr.indexOf(obj) !== -1) {
console.log(arr)
return arr
} else {
arr.map(item => {
if (item.children) findDeep(item.children, obj)
})
}
}
And use the new function like this:
const newArr = findDeep(colors.children, color)
newArr.splice(newArr.indexOf(color) + 1, 0, newColor)
But I get this error:
bundle.js:19893 Uncaught TypeError: Cannot read property 'splice' of undefined
What I'm a doing wrong?
(Note: Here's the CodePen.)
(Note 2: console.log(arr) does return the nested children. But for some reason they become undefined outside of the function.)
You are not returning you recursive findDeep method within the map. Return that for the recursion to work because your conditional branch is not returning anything from within map. As a result you are getting the result as undefined. JSBin
First, a find method that will return the array in which the requested item is located (as a direct child).
function findDeep(arr, obj) {
return arr.map((item) => {
if (item.name === obj.name) {
return arr;
} else if (item.children) {
return findDeep(item.children, obj);
} else {
return undefined;
}
}).reduce((prev, cur) => {
return prev ? prev : cur;
});
}
You could use that to append items to the list, but that will still modify the original array:
function appendDeep(arr, color, newColor) {
let found = findDeep(arr, color);
if (found) {
found.splice(found.indexOf(color) + 1, 0, newColor);
}
return arr;
}
If you don't want to modify the original array, things get more complex. That's because the standard array functions such as push and splice will modify the original array. There's no quick fix, at least not that I know of, because preferably you would not want to clone any more items than you really have to.
You don't need to clone black, but you do need to clone the array that contains it (It can simply reuse the existing object for black.) That means the object for yellow also needs to be cloned (to use the cloned array) and the array in which yellow is located needs to be cloned. But white, which is in the same array, is not modified and does not need to be cloned. I've not figured out how to do that properly.
This is a proposal which uses thisArgs of Array#some.
function appendDeep(object, search, insert) {
function iter(a) {
if (a.name === search.name) {
this.children.push(insert);
return true;
}
return Array.isArray(a.children) && a.children.some(iter, a);
}
object.children.some(iter, object);
}
var colors = { children: [{ name: 'white', }, { name: 'yellow', children: [{ name: 'black' }] }] },
color = { name: 'black' },
newColor = { name: 'brown' };
appendDeep(colors, color, newColor);
document.write('<pre>' + JSON.stringify(colors, 0, 4) + '</pre>');

Categories