how to map 2 arrays using 1 common json field (nested array) - javascript

I have 2 arrays that i am returning...in this case Employments and Users. They both have a common field 'id' and i want to use that field to map.
I can use a for loop to map it currently but since i am looping over a nested array...i only get to catch the mapping for the first part of the array.
my json objects:
$scope.Users = [{
id: 1,
name: "Ryan"
}, {
id: 2,
name: "Julie"
}, {
id: 3,
name: "Stu"
},
{
id: 4,
name: "Holly"
}];
$scope.Employments = [{
categoriesBag: [
{
category: [
{
user_id: 1,
title: "manager"
},
{
user_id: 2,
title: "student"
}
]
},
{
category: [
{
user_id: 3,
title: "worker"
},
{
user_id: 4,
title: "facilty"
}
]
}
]
}];
the for loop that i am using to map the data:
$scope.getCategory = function(id) {
var employmentCategories = $scope.Employments.categoriesBag[0].category;
for (var i = 0; i < employmentCategories[0].category.length; i++) {
if (employmentCategories[0].category[i].user_id === id) {
return employmentCategories[0].category[i].title;
}
}
};
since i am specifying that i only want the first array employmentCategories[0], the other two users are not included in the for loop. Is there a way for me to do a loop inside of a loop to loop over only the nested categories?

You can use a nested loop
$scope.getCategory = function(id) {
for (bag in $scope.Employments.categoriesBag) {
for (category in bag.category) {
if (category.user_id == id){
return category.title
}
}
}
}

Related

Filtering an array of objects Vue

I have an array of objects and Im trying to filter by matching ids
//Vehicle is added to quote
function filterByID(item) {
return item.id === 1;
}
this.vehicle = this.data.filter(filterByID);
data is as follows:
data: [
0: {
id: 0,
name: name
},
1: {
id: 1,
name: name
},
2: {
id: 2,
name: name
}
]
Im getting an empty error when I check the vehicle part
Are you using it like this:
const data = [
{
id: 0,
name: '',
},
{
id: 1,
name: '',
},
{
id: 2,
name: '',
},
];
function filterByID(item) {
return item.id === 1;
}
console.log(data.filter(filterByID)); // output: [{ "id": 1, "name": "" }]
You don't always need to define a separate function, you can use an arrow function, as below.
const data = [{
id: 0,
name: name
},
{
id: 1,
name: name
},
{
id: 2,
name: name
}
]
const vehicle = data.filter(item => item.id === 1);
console.log(vehicle);
This works fine in pure JS, it looks like it might be an issue with the lifecycle or state of your application. Use console.log to make sure that this.data is what you expect it to be

Delete object or nested object from array

I need to delete an object or nested object in an array, from a given object ID.
The object needed to be deleted can both be a root object in the array or a nested object (a variant in this example) in one of the root objects.
Here's the array structure (both root objects and variant objects has unique IDs):
[
{ id: 1, title: 'object without variants', variants: [] },
{ id: 2, title: 'object with variants', variants: [{ id: 21, title: 'variant 1' }, { id: 22, title: 'variant 2' }]
]
So for example if the object ID passed from the click event that triggers the delete function is 1, I want to delete the whole root object with the ID of 1 and if the object passed from the click event is 21, I only want to delete the variant with the ID of 21 under the root object with the ID of 2 and not the whole root object.
How can this be done?
UPDATE
I got it working by using this code (passedObjectId is the ID of the object to be removed):
array = array.filter(object => object.id !== passedObjectId);
for (let object of array) {
object.variants = object.variants.filter(variant => variant.id !== passedObjectId);
}
I also need to remove the root object from the array if the last variant is removed from the object.
The code below works, but can I make this any prettier without having to use 3 filter() methods?
array = array.filter(object => object.id !== passedObjectId);
for (let object of array) {
// Remove the variant from the root object
object.variants = object.variants.filter(variant => variant.id !== passedObjectId);
// Remove the root object, if there's no variants left in it
if (!object.variants.length) {
array = array.filter(object => object.id !== passedObjectId);
}
}
ANOTHER UPDATE
I ended up using this code, that also removes a root object, if the last variant is removed:
array = array.filter(object => {
const hasRemovedVariant = object.variants.some(variant => variant.id === passedObjectId);
if (hasRemovedVariant) {
object.variants = object.variants.filter(variant => variant.id !== passedObjectId);
return object.variants.length;
}
return object.id !== passedObjectId;
});
Here is an example on how you can delete them separately, I let you put that together. If you have any question or trouble in the way to do it, feel free to ask.
const original = [{
id: 1,
title: 'object without variants',
variants: [],
},
{
id: 2,
title: 'object with variants',
variants: [{
id: 21,
title: 'variant 1'
}, {
id: 22,
title: 'variant 2'
}],
},
{
id: 3,
title: 'object with one variant',
variants: [{
id: 21,
title: 'variant 1'
}],
}
];
// Remove the root id
const rootIdToDelete = 1;
const modifiedRoot = original.filter(x => x.id !== rootIdToDelete);
// Remove the variant id
const variantToDelete = 21;
const modifiedRootAndVariant = modifiedRoot.filter((x) => {
x.variants = x.variants.filter(x => x.id !== variantToDelete);
// Keep only the roots that have at least 1 variant
return x.variants.length;
});
console.log(modifiedRootAndVariant);
You need to loop through your array and check if each object id is a match if it is the delete the object, else loop through your variants within the object and check for a match and delete the object.
Make sure you loop in reverse as you are modifying the array that you are looping through, hence the indexes change and you might get index out range exception
var myArray = getYourArray();
var idToCheck = 1; // get the id
for(int i=myArray.length;i--){
if(myArray[i].id == idToCheck){
myArray.splice(i);
}
else{
if(myArray[i].variants.length>0){
for(int j=myArray[i].variants.length;j--){
if(myArray[i].variants[j].id == idToCheck){
myArray[i].variants.splice(j);
}
}
}
}
}
This snippet will remove any child with an specified ID, no matter how big the hierarchy is.
var myArray = [{
id: 1,
title: 'object without variants',
variants: []
},
{
id: 2,
title: 'object with variants',
variants: [{
id: 21,
title: 'variant 1',
variants: [{
id: 23,
title: 'variant 1'
}, {
id: 24,
title: 'variant 2'
}]
}, {
id: 22,
title: 'variant 2'
}]
}
]
console.log(deleteByID(myArray, 21));
function deleteByID(array, id) {
for (var i = 0; i < array.length; i++) {
var item = array[i];
deleteItemByID(myArray, item, id, i);
}
return array;
}
function deleteItemByID(array, item, id, count) {
if (item.id == id) {
array.splice(count, 1);
return;
} else {
if (item.variants) {
if (typeof item.variants === "object") {
for (var i = 0; i < item.variants.length; i++) {
var varItem = item.variants[i];
deleteItemByID(item.variants, varItem, id, i);
}
}
}
}
}
You need to have a loop inside a loop.
You could use forEach for example.
var arr = [
{ id: 1, title: 'object without variants', variants: [] },
{ id: 2, title: 'object with variants', variants: [{ id: 21, title: 'variant 1' }, { id: 22, title: 'variant 2' }]}
];
var idToDelete = 21;
arr.forEach(function(obj,i){
if (obj.id == idToDelete){
arr.splice(i,1);
}
obj.variants.forEach(function(variants,i){
if (variants.id == idToDelete){
obj.variants.splice(i,1);
}
})
})
console.log(arr)
People have already answered but how about some functional, recursive and immutable code:
let array = [
{ id: 1, title: 'object without variants', variants: [] },
{ id: 2, title: 'object with variants', variants: [{ id: 21, title: 'variant 1' }, { id: 22, title: 'variant 2' }] }
];
const deleteFromArray = (arr, id) => {
if (!arr) return
let res = [];
arr.forEach((obj) => {
if (obj.id !== id) {
res.push({ ...obj, variants: deleteFromArray(obj.variants, id) })
}
})
return res;
}
console.log(JSON.stringify(deleteFromArray(array, 1)));
That way if you have some reference to the deleted object, it is deleted in ALL the objects in your array, including any nested variants.
You could create recursive function with some method so that you can exit the loop on first match and you can use splice to remove the element.
const data = [{ id: 1, title: 'object without variants', variants: [] },{ id: 2, title: 'object with variants', variants: [{ id: 21, title: 'variant 1' }, { id: 22, title: 'variant 2' }]}]
function remove(data, oid) {
data.some((e, i) => {
if(oid == e.id) return data.splice(i, 1);
if(e.variants) remove(e.variants, oid)
})
}
remove(data, 21)
console.log(data)

Return deconstructed array in object with es6 map

I want to return a deconsturcted array so I only get single element in te returned array instead of array.
const data = [
{
title: 'amsterdam',
components: [
{
id: 1,
name: 'yanick',
},
{
id: 2,
name: 'ronald',
},
],
},
{
title: 'rotterdam',
components: [
{
id: 4,
name: 'nicky',
},
{
id: 3,
name: 'casper',
},
],
},
];
const test = data
.map(item => {
console.log(item.components);
return item.components;
}).map(array => {
// how to get comibned components here?
// it can't use ...item.components (deconstructing or something)
});
console.log('test', test);
So I want to use chained map functions to create one array of all elements in item.components. Is this possible? Seems like I can't deconstruct the array of each item.
Array.prototype.reduce seems like the correct method to use in this context.
const test = data.reduce( (result, current) => result.concat(current.components) , []);
console.log('test', test);
Output
test [ { id: 1, name: 'yanick' },
{ id: 2, name: 'ronald' },
{ id: 4, name: 'nicky' },
{ id: 3, name: 'casper' } ]
Get the components with Array.map(), and flatten by spreading into Array.concat():
const data = [{"title":"amsterdam","components":[{"id":1,"name":"yanick"},{"id":2,"name":"ronald"}]},{"title":"rotterdam","components":[{"id":4,"name":"nicky"},{"id":3,"name":"casper"}]}];
const result = [].concat(...data.map(o => o.components));
console.log(result);
To get data combined into single array you can use reduce in combination with concat that will create a single array of results.
const data = [
{
title: 'amsterdam',
components: [
{
id: 1,
name: 'yanick',
},
{
id: 2,
name: 'ronald',
},
],
},
{
title: 'rotterdam',
components: [
{
id: 4,
name: 'nicky',
},
{
id: 3,
name: 'casper',
},
],
},
];
const test = data
.map(item => {
return item.components;
}).reduce((res, item) => {
return res.concat(item);
}, []);
console.log('test', test);

Removing duplicate array values and then storing them [react]

I'm trying to strip the duplicate array values from my current array. And I'd like to store the fresh list (list without duplicates) into a new variable.
var names = ["Daniel","Lucas","Gwen","Henry","Jasper","Lucas","Daniel"];
const uniqueNames = [];
const namesArr = names.filter((val, id) => {
names.indexOf(val) == id; // this just returns true
});
How can I remove the duplicated names and place the non-duplicates into a new variable?
ie: uniqueNames would return...
["Daniel","Lucas","Gwen","Henry","Jasper"]
(I'm using react jsx) Thank you!
You can do it in a one-liner
const uniqueNames = Array.from(new Set(names));
// it will return a collection of unique items
Note that #Wild Widow pointed out one of your mistake - you did not use the return statement. (it sucks when we forget, but it happens!)
I will add to that that you code could be simplified and the callback could be more reusable if you take into account the third argument of the filter(a,b,c) function - where c is the array being traversed. With that said you could refactor your code as follow:
const uniqueNames = names.filter((val, id, array) => {
return array.indexOf(val) == id;
});
Also, you won't even need a return statement if you use es6
const uniqueNames = names.filter((val,id,array) => array.indexOf(val) == id);
If you want to remove duplicate values which contains same "id", You can use this.
const arr = [
{ id: 2, name: "sumit" },
{ id: 1, name: "amit" },
{ id: 3, name: "rahul" },
{ id: 4, name: "jay" },
{ id: 2, name: "ra one" },
{ id: 3, name: "alex" },
{ id: 1, name: "devid" },
{ id: 7, name: "sam" },
];
function getUnique(arr, index) {
const unique = arr
.map(e => e[index])
// store the keys of the unique objects
.map((e, i, final) => final.indexOf(e) === i && i)
// eliminate the dead keys & store unique objects
.filter(e => arr[e]).map(e => arr[e]);
return unique;
}
console.log(getUnique(arr,'id'))
Result :
[
{ id: 2, name: "sumit" },
{ id: 1, name: "amit" },
{ id: 3, name: "rahul" },
{ id: 4, name: "jay" },
{ id: 7, name: "sam" }
]
you forgot to use return statement in the filter call
const namesArr = duplicatesArray.filter(function(elem, pos) {
return duplicatesArray.indexOf(elem) == pos;
});
Since I found the code of #Infaz 's answer used somewhere and it confused me greatly, I thought I would share the refactored function.
function getUnique(array, key) {
if (typeof key !== 'function') {
const property = key;
key = function(item) { return item[property]; };
}
return Array.from(array.reduce(function(map, item) {
const k = key(item);
if (!map.has(k)) map.set(k, item);
return map;
}, new Map()).values());
}
// Example
const items = [
{ id: 2, name: "sumit" },
{ id: 1, name: "amit" },
{ id: 3, name: "rahul" },
{ id: 4, name: "jay" },
{ id: 2, name: "ra one" },
{ id: 3, name: "alex" },
{ id: 1, name: "devid" },
{ id: 7, name: "sam" },
];
console.log(getUnique(items, 'id'));
/*Output:
[
{ id: 2, name: "sumit" },
{ id: 1, name: "amit" },
{ id: 3, name: "rahul" },
{ id: 4, name: "jay" },
{ id: 7, name: "sam" }
]
*/
Also you can do this
{Array.from(new Set(yourArray.map((j) => j.location))).map((location) => (
<option value={`${location}`}>{location}</option>
))}

How can I get the delta from two JSON arrays?

Below are two JSON arrays. I want to get delta data (compare the two datasets and return elements that don't appear in both sets).
var data1 = [
{ id: 1, name: "Normal" },
{ id: 2, name: "Admin" }
];
var data2 = [
{ id: 1, name: "Normal" },
{ id: 2, name: "Admin" },
{ id: 3, name: "HR" },
{ id: 4, name: "finance" }
];
expected output:
var Result = [
{ id: 3, name: "HR" },
{ id: 4, name: "finance" }
];
I have tried this but didn't have any luck:
$.grep(data2, function (el) {
if ($.inArray(el, data1) == -1)
diff.push([el, IDl]);
});
You are close, the problem is you need to do a deep compare of your objects. inArray will only do a shallow compare. The following code will do a deep compare by checking equality of id and name. Also, it allows jQuery.grep to build the resulting array so you do not need to do this manually.
var data1 = [
{ id: 1, name: "Normal" },
{ id: 2, name: "Admin" }
];
var data2 = [
{ id: 1, name: "Normal" },
{ id: 2, name: "Admin" },
{ id: 3, name: "HR" },
{ id: 4, name: "finance" }
];
function compare(data1, data2) {
return $.grep(data2, function(el) {
return !data1.some(function(elToCompare) {
return elToCompare.id === el.id && elToCompare.name === el.name;
});
});
}
$("#output").text(JSON.stringify(compare(data1, data2)));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js"></script>
<div id="output"></div>

Categories