I have currently an object which contains a array on which I can iterate.
====== First Step =====
Iterate over an object's attribute instead of the object itself
I am wondering if there's a way to use :
const obj = new YMap();
obj.forEach(...);
instead of
const obj = new YMap();
obj.map.forEach();
EDIT : From VLAZ Comment, I can add this :
this.forEach = function(f) { this.maps.forEach(f);}
In my constructor so my forEach iterate over the maps attribute of my
object and not over my object itself.
===== Second Step =====
Avoid forEach imbrication to parse an object attribute of type Array of Map/List/Array
By extends, if I add a little more complexity on my object so it doesn't have an array but an array of list, is there a way to do :
const obj = new YMap();
obj.forEach(...);
instead of
const obj = new YMap();
obj.maps.foreach( function(map) {
map.forEach( function(item) {
...
}
});
To explain it differently, I wonder if there's a way that exist that allow me to have this data structure :
{
maps : [
[ 'A', 'B' ],
[ 'C', 'D' ],
[ 'E', 'F' ]
]
}
Where I could use the code :
obj.forEach(function(item) { console.log(item) });
that would output :
A B C D E F
without having to iterate over the array of array and then iterate on the items of each array using an imbricated forEach
My object looks like the following :
function YMap() {
this.maps = [new HashMap()];
Object.defineProperty(YMap.prototype, 'length', {get: function() {
...
}});
}
YMap.prototype.add = function() {...}
YMap.prototype.get = function() {...}
[...]
module.exports = YMap;
you can create custom iterable to loop onto object. I have created one using class based approach with help of Symbol.iterator property.
class YMap {
constructor() {
this.maps = [];
}
[Symbol.iterator]() {
let index = 0;
const that = this;
return {
next() {
let value = undefined;
let done = true;
if (index < that.maps.length) {
value = [...that.maps[index++]].join(" ")
done = false;
}
return {
value,
done
};
}
};
}
add(item) {
this.maps.push(item);
}
}
const obj = new YMap();
obj.add([23, 21]);
obj.add([25, 29]);
for (let c of obj) {
console.log(c, );
}
Related
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;
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 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 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
})
}
I have an object array containing one property call name like this
var nameArr = [
{
"name":"john"
},
{
"name":"carl"
},
{
"name":"peter"
}
]
I have a another array called ageArr and it only contain property called age
var ageArr = [
{
"age":"22"
},
{
"age":"21"
},
{
"age":"32"
}
]
i want to concat these array and end result should result like this
var result = [
{
"age":"22",
"name":"john"
},
{
"age":"21",
"name":"carl"
},
{
"age":"32",
"name":"peter"
}
]
note that length of the both arrays always equal and dynamic. Is there any way i can do this without looping these array inside one another. Thank you.
You can use Object.assign() and map() and return new array.
var nameArr = [{"name":"john"},{"name":"carl"},{"name":"peter"}]
var ageArr = [{"age":"22"},{"age":"21"},{"age":"32"}]
var result = nameArr.map(function(e, i) {
return Object.assign({}, e, ageArr[i])
})
console.log(result)
Single forEach() function is enough.
var nameArr=[{name:"john"},{name:"carl"},{name:"peter"}],
ageArr=[{age:"22"},{age:"21"},{age:"32"}];
nameArr.forEach((v,i) => v.age = ageArr[i].age)
console.log(nameArr);
You can use the following code snippet.
var result = nameArr.map(function( obj, index ) {
var res = ageArr[index];
res.name = obj.name;
return res;
});
In the map, you can easily use
jQuery.extend()
to create a merge of two same index object.