I have 2 arrays. one is costHeadArray which is as
[
{
"label":"Flight",
"value":"179daf5f-2f89-1d12-40a2-e91a6a732774"
}
]
I am iterating response. My object value is as
{
taxInvoiceId: "b1b4d9ec-f20c-4a33-94c1-af7589e7b15a",
enquiryId: "3db0b4a8-5ab1-4c31-861a-73df36205716",
costHeadId: "179daf5f-2f89-1d12-40a2-e91a6a732774",
costSheetId: "01edd4a1-f1ad-a836-3a19-3a28271f1b57",
...
}
Now in same iteration, I need to check if costHeadArray value matches value in object. costHeadArray can have multiple json objects. only if value in costHeadArray matches with object then i need to enable flag. I tried using includes but it dont works . might be I am doing something wrong
My code
this.airInvoiceService.getTaxInvoiceLog(this.enquiryId).subscribe(
response => {
this.taxInvoiceLogs.unshift(...response);
for(const a of response) {
// {taxInvoiceId: "b1b4d9ec-f20c-4a33-94c1-af7589e7b15a", enquiryId: "3db0b4a8-5ab1-4c31-861a-73df36205716", enquiryRefId: "MTPL-2021-000074", costHeadId: "179daf5f-2f89-1d12-40a2-e91a6a732774", costSheetId: "01edd4a1-f1ad-a836-3a19-3a28271f1b57", …}
console.log(a);
//[{"label":"Flight","value":"e9bf7aa2-c730-8175-1cec-afcd239b0dff"}]
console.log(JSON.stringify(this.costHeadArray));
console.log(a.costHeadId);
if (Object.values(a).includes(this.costHeadArray)){
console.log('Gokul');
}
if (this.costHeadArray.includes(a.costHeadId)) {
console.log('Sam');
this.hideForm = true;
}
}
I have write simple script for you, please have a look, I hope it will help you
let costHeadArray = [
{
"label":"Flight",
"value":"179daf5f-2f89-1d12-40a2-e91a6a732774"
}
];
let myObject = {
taxInvoiceId: "b1b4d9ec-f20c-4a33-94c1-af7589e7b15a",
enquiryId: "3db0b4a8-5ab1-4c31-861a-73df36205716",
costHeadId: "179daf5f-2f89-1d12-40a2-e91a6a732774",
costSheetId: "01edd4a1-f1ad-a836-3a19-3a28271f1b57",
}
let hasCostHeadValue = costHeadArray.find(o => o.value == myObject.costHeadId) != null;
console.log(hasCostHeadValue);
///////////////////////////////////////////////////////////////////////////////////
Update your method like this.
this.airInvoiceService.getTaxInvoiceLog(this.enquiryId).subscribe(
response => {
this.taxInvoiceLogs.unshift(...response);
for(const a of response) {
// {taxInvoiceId: "b1b4d9ec-f20c-4a33-94c1-af7589e7b15a", enquiryId: "3db0b4a8-5ab1-4c31-861a-73df36205716", enquiryRefId: "MTPL-2021-000074", costHeadId: "179daf5f-2f89-1d12-40a2-e91a6a732774", costSheetId: "01edd4a1-f1ad-a836-3a19-3a28271f1b57", …}
console.log(a);
//[{"label":"Flight","value":"e9bf7aa2-c730-8175-1cec-afcd239b0dff"}]
console.log(JSON.stringify(this.costHeadArray));
console.log(a.costHeadId);
let hasCostHeadValue = this.costHeadArray.find(o => o.value == a.costHeadId) != null;
if(hasConstHeadValue){
} else {
}
// if (Object.values(a).includes(this.costHeadArray)){
// console.log('Gokul');
// }
// if (this.costHeadArray.includes(a.costHeadId)) {
// console.log('Sam');
// this.hideForm = true;
//}
}
Array.includes is comparing the array items by reference, not by value, thus:
For the array const values = [ {value: 1}, { value: 2} ]
values.includes(values[0]) will return true, because it re-uses the same {value:1}-object as the array and both objects have same reference ID.
values.includes({value: 1}) will return false, because the objects have the same content but different references.
You can use Array.findIndex instead:
const search = {value: 1};
const includes = values.findIndex(item => item.value === search.value) >= 0;
Related
Problem
I would like to have the below two JSON combined together using the ID and have the expected result as mentioned below. I have tried a few solutions that were available but none worked for my use case. Any suggestions will be great !!
Tried to do:
How to merge two json object values by id with plain Javascript (ES6)
Code
var json1 = [
{
"id":"A123",
"cost":"5020.67",
"fruitName":"grapes"
},
{
"id":"A456",
"cost":"341.30",
"fruitName":"apple"
},
{
"id":"A789",
"cost":"3423.04",
"fruitName":"banana"
}
];
var json2 = [
{
"id":"A123",
"quantity":"7"
},
{
"id":"A789",
"quantity":"10"
},
{
"id":"ABCD",
"quantity":"22"
}
];
Below is the code I tried:
var finalResult = [...[json1, json2].reduce((m, a) => (a.forEach(o => m.has(o.id) && Object.assign(m.get(o.id), o) || m.set(o.id, o)), m), new Map).values()];
Expected result:
[
{
"id":"A123",
"cost":"5020.67",
"fruitName":"grapes",
"quantity":"7"
},
{
"id":"A456",
"cost":"341.30",
"fruitName":"apple"
},
{
"id":"A789",
"cost":"3423.04",
"fruitName":"banana",
"quantity":"10"
},
{
"id":"ABCD",
"quantity":"22"
}
]
You can accomplish this fairly easily without getting too fancy. Here's the algorithm:
Put the items from json1 into an object by id, so that you can look them up quickly.
For each item in json2: If it already exists, merge it with the existing item. Else, add it to objectsById.
Convert objectsById back to an array. I've used Object.values, but you can also do this easily with a loop.
var json1 = [
{
"id":"A123",
"cost":"5020.67",
"fruitName":"grapes"
}, {
"id":"A456",
"cost":"341.30",
"fruitName":"apple"
}, {
"id":"A789",
"cost":"3423.04",
"fruitName":"banana"
}
];
var json2 = [
{
"id":"A123",
"quantity":"7"
}, {
"id":"A789",
"quantity":"10"
}
];
const objectsById = {};
// Store json1 objects by id.
for (const obj1 of json1) {
objectsById[obj1.id] = obj1;
}
for (const obj2 of json2) {
const id = obj2.id;
if (objectsById[id]) {
// Object already exists, need to merge.
// Using lodash's merge because it works for deep properties, unlike object.assign.
objectsById[id] = _.merge(objectsById[id], obj2)
} else {
// Object doesn't exist in merged, add it.
objectsById[id] = obj2;
}
}
// All objects have been merged or added. Convert our map to an array.
const mergedArray = Object.values(objectsById);
I think a few steps are being skipped in your reduce function. And it was a little difficult to read because so many steps are being combined in one.
One critical piece that your function does not account for is that when you add 2 numerical strings together, it concats the strings.
const stringTotal = "5020.67" + "3423.04" // result will be "5020.673423.04"
The following functions should give you the result you are looking for.
// calculating the total cost
// default values handles cases where there is no obj in array 2 with the same id as the obj compared in array1
const calcualteStringTotal = (value1 = 0, value2 = 0) => {
const total = parseFloat(value1) + parseFloat(value2)
return `${total}`
}
const calculateTotalById = (array1, array2) => {
const result = []
// looping through initial array
array1.forEach(outterJSON => {
// placeholder json obj - helpful in case we have multiple json in array2 with the same id
let combinedJSON = outterJSON;
// looping through second array
array2.forEach(innerJSON => {
// checking ids
if(innerJSON.id === combinedJSON.id) {
// calls our helper function to calculate cost
const updatedCost = calcualteStringTotal(innerJSON.cost, outterJSON.cost)
// updating other properties
combinedJSON = {
...outterJSON,
...innerJSON,
cost: updatedCost
}
}
})
result.push(combinedJSON)
})
return result
}
const combinedResult = calculateTotalById(json1, json2)
I figured that by using reduce I could make it work.
var finalResult = [...[json1, json2].reduce((m, a) => (a.forEach(o => m.has(o.id) && Object.assign(m.get(o.id), o) || m.set(o.id, o)), m), new Map).values()];
I've an array of errors, each error has a non-unique param attribute.
I'd like to filter the array based on whether the param has been seen before.
Something like this:
const filteredErrors = [];
let params = [];
for(let x = 0; x < errors.length; x++) {
if(!params.includes(errors[x].param)) {
params.push(errors[x].param);
filteredErrors.push(errors[x]);
}
}
But I've no idea how to do this in ES6.
I can get the unique params const filteredParams = Array.from(new Set(errors.map(error => error.param)));
but not the objects themselves.
Pretty sure this is just a weakness in my understanding of higher order functions, but I just can't grasp it
You could destrucure param, check against params and add the value to params and return true for getting the object as filtering result.
As result you get an array of first found errors of the same type.
const
params = [],
filteredErrors = errors.filter(({ param }) =>
!params.includes(param) && params.push(param));
Instead of an array you can make use of an object to keep a map of existing values and make use of filter function
let params = {};
const filteredErrors = errors.filter(error => {
if(params[error.param]) return false;
params[error.param] = true;
return true;
});
i'd probably do it like this with a reduce and no need for outside parameters:
const filteredErrors = Object.values(
errors.reduce((acc, val) => {
if (!acc[val.param]) {
acc[val.param] = val;
}
return acc;
}, {}))
basically convert it into an object keyed by the param with the object as values, only setting the key if it hasn't been set before, then back into an array of the values.
generalized like so
function uniqueBy(array, prop) {
return Object.values(
array.reduce((acc, val) => {
if (!acc[val[prop]]) {
acc[val[prop]] = val;
}
return acc;
}, {}))
}
then just do:
const filteredErrors = uniqueBy(errors, 'param');
If your param has a flag identifier if this param has been seen before then you can simply do this.
const filteredErrors = errors.filter(({ param }) => param.seen === true);
OR
const filteredErrors = errors.filter((error) => error.param.seen);
errors should be an array of objects.
where param is one of the fields of the element of array errors and seen is one of the fields of param object.
You can do it by using Array.prototype.reduce. You need to iterate through the objects in the array and keep the found params in a Set if it is not already there.
The Set.prototype.has will let you find that out. If it is not present in the Set you add it both in the Set instance and the final accumulated array, so that in the next iteration if the param is present in your Set you don't include that object:
const errors = [{param: 1, val: "err1"}, {param: 2, val: "err2"}, {param: 3, val: "err3"}, {param: 2, val: "err4"}, {param: 1, val: "err5"}];
const { filteredParams } = errors.reduce((acc, e) => {
!acc.foundParams.has(e.param) && (acc.foundParams.add(e.param) &&
acc.filteredParams.push(e));
return acc;
}, {foundParams: new Set(), filteredParams: []});
console.log(filteredParams);
I have an array of objects that looks something like
{
"foo":[
{
"bar":"boo"
},
{
"baz":"bang"
}
]
}
I want to update baz with a new value but I cannot work out how to merge both these objects?
I tried something like Object.assign({}, foo,{baz: 'beep'})
But this did not work?
foo is an Array of objects to replace it with new value, try the following:
var obj = { "foo":[ { "bar":"boo" }, { "baz":"bang" } ] };
var index = obj.foo.findIndex((o) =>Object.keys(o).includes("baz"));
if(index != -1)
Object.assign(obj.foo[index], {baz: 'beep'});
console.log(obj);
Assuming you don't necessarily know the array index of the element you're trying to modify, you'll need to search through that array to find it -- for example using Array.find():
let quux = {
"foo":[
{
"bar":"boo"
},
{
"baz":"bang"
}
]
}
quux.foo.find(
(obj) => {
return obj.baz === "bang"
}
).baz="beep";
console.log(quux);
// This mutates the original object; if you need a clone wrap this in an Object.assign as shown in other answers
(Arrays of objects can be inconvenient for this reason; you may be better off with an object of objects, so there's an ID for each.)
If you're trying to create a modified copy of foo, use Array.prototype.map:
const foo = [
{
"bar":"boo"
},
{
"baz":"bang"
}
];
const newFoo = foo.map(value => {
if('baz' in value) return Object.assign({}, value, {a: 'b'});
return value;
});
console.log(newFoo);
I'm having trouble understanding the behavior of this javascript code.
const devices = searchResult.results.forEach(device => {
const temp = Object.keys(device.fields);
for(var property in temp) {
if(device.fields.hasOwnProperty(property)) {
if (!usedPropertiesAcrossModels.has(property)) {
delete device.fields[property];
}
}
}
}
I am trying to delete the keys if a javascript object that do not belong to a set. I have stepped through the debugger, and I know that there is only one element in the set and 15 elements in device.fields. No matter what, nothing is being deleted from device.fields, I have no idea why. Moreover, temp seems to be undefined until I am out of the loop. Property is always undefined even though there are items in temp! This doesn't make any sense.
searchResult = {};
searchResult.results = [{
fields:{
name: 'hello',
type:'gekko',
random:'randomString'
}
}
]
usedPropertiesAcrossModels = {
name: 'hello',
random:'hello'
}
const devices = searchResult.results.forEach(device => {
const
temp = Object.keys(device.fields).map((property)=>{
if(device.fields.hasOwnProperty(property)) {
if (!usedPropertiesAcrossModels.hasOwnProperty(property)) {
delete device.fields[property];
}
}
})
})
console.log(searchResult)
Using map fixed the issue as in your case the for in was giving index instead of the keys of the object.Or as martin said you can consider using for of as well.
const temp = Object.keys(o) will give you array of object's keys. You should use for of loop instead of for in, as you need to iterate through values of it, not their keys in temp object:
const o = { a: 1, b: 2, c: 3 };
const temp = Object.keys(o);
console.log(temp);
// this will iterate through `temp` keys, so 0, 1, 2
for (const property in temp) {
console.log('wrong:', property);
}
// this will iterate through `temp` values, so 'a', 'b', 'c'
for (const property of temp) {
console.log('correct:', property);
}
// or you could iterate via `forEach()`
temp.forEach(property => {
console.log('correct:', property);
});
Also with for of loop, you do not need the hasOwnProperty check.
I have a JS Object that may look like one of the following:
// Example #1:
var data = {
product: {
value1: 'v1',
value2: 2
}
}
// Example #2:
var data = {
order: {
value1: 1
}
}
// Example #3:
var data = {
value1: 1
}
What I'd like to achieve:
var inputName = 'product[value1]';
var data = {
product: {
value1: 'v1',
value2: 2
}
}
var value = something(data, inputName); // should return v1
inputName and data can change, I can have any of the above data Objects with an inputName like 'product[value2]', 'order[value1]', 'value1'.
My guess is to use regex and get both attribute names. Is the a better way?
you can use underscore js _.each to iterate over the object like
_.each(data ,function(product){
console.log(product.value);
});
see the link: http://underscorejs.org/#each
you can also use for each loop.
Also you can perform filter like below:
_.filter(data, function(product){
return product.value;
});
One other way is to create a dictionary that directly caters to your search.
You can flatten your multi-level key value pair to create a dictionary that you can use readily. The below flatten function (taken from here) creates a dictionary like:
{
"product.value1": "v1",
"product.value2": 2
}
You can then just query it using dictionary["product.value1"]
This flatten function can be altered to format the keys like product[value1] if you wish so.
var data = {
product: {
value1: 'v1',
value2: 2
}
}
var myDictionary = flatten(data);
console.log(myDictionary);
console.log(myDictionary["product.value1"]);
console.log(myDictionary["product.something else"]);
function flatten(obj, opt_out, opt_paths) {
var out = opt_out || {};
var paths = opt_paths || [];
return Object.getOwnPropertyNames(obj).reduce(function(out, key) {
paths.push(key);
if (typeof obj[key] === 'object') {
flatten(obj[key], out, paths);
} else {
out[paths.join('.')] = obj[key];
}
paths.pop();
return out;
}, out)
}
If you know possible attribute names, then I would define an array with possible attribute names, then iterate over them checking if there is a field with this name
const names = [
'product',
'order'
];
function findValue(data){
if(data.value1) return data.value1;
for(let name in names){
if(data[name].value1) return data[name].value1;
}
}
Explanation
If you want to give your function a string like 'product[value1]' as argument you need to get all attribute values that you need to query for your result value. I did it with query.replace(/(\[)|(\])/g, ' ')split(' '). The returning array you need to check for empty strings and remove them. I did it with filter.
After that you can simply use reduce on the returned array to get on each iteration the new value. In the last iteration you have your result.
Code
function getDataValue(obj, query) {
var attributes = getAttributeNames(query)
return attributes.reduce(function(value, current) {
return value[current]
}, obj)
}
function getAttributeNames(query) {
return query.replace(/(\[)|(\])/g, ' ')
.split(' ')
.filter(function(string) {
return string.length > 0
})
}
Example
var dataOne = {
product: {
value1: 'v1',
value2: 2
}
}
var dataTwo = {
product: {
subProduct: {
value1: 'v2'
}
}
}
console.log(getDataValue(dataOne, 'product[value1]'))
console.log(getDataValue(dataTwo, 'product[subProduct][value1]'))
function getDataValue(obj, query) {
var attributes = getAttributeNames(query)
return attributes.reduce(function(value, current) {
return value[current]
}, obj)
}
function getAttributeNames(query) {
return query.replace(/(\[)|(\])/g, ' ')
.split(' ')
.filter(function(string) {
return string.length > 0
})
}