I want to merge item and purchases array of json into one by matching their property value.
Here's the source :
{
"item": [
{
"invoiceId": 1
},
{
"invoiceId": 2
},
{
"invoiceId": 3
}
],
"purchase": [
{
"id": "1",
"date": "12/1/2014"
},
{
"id": "2",
"date": "12/1/2014"
},
{
"id": "3",
"date": "12/1/2014"
}
]
}
I want to produce something like this :
{
"combined": [
{
"invoiceId": 1,
"id": "1",
"date": "12/1/2014"
},
{
"invoiceId": 2,
"id": "2",
"date": "12/1/2014"
},
{
"invoiceId": 3,
"id": "3",
"date": "12/1/2014"
}
]
}
How can I match the item.invoiceId with purchase.id?
Solution
assuming obj is your object
var new_obj = {combined:[]};
obj["purchase"].forEach(function(a) {
obj["item"].forEach(function(b){
if (+b["invoiceId"]===(+a["id"])) {
a["invoiceId"] = b["invoiceId"] || 0;//WILL MAKE INVOICEID 0 IF IT IS NOT DEFINE. CHANGE 0 TO YOUR NEEDS
new_obj.combined.push(a);
}
});
});
How it works
The first .forEach() loops through obj.purchase. Then we loop through obj.item To check if their is a matching invoiceId (if you don't need to make sure their is a matching invoiceId, use the alternate code). Then, we simply add a new value to the new_obj
The result (copied from console) is:
{
"combined":[
{
"id":"1",
"date":"12/1/2014",
"invoiceId":1
},
{
"id":"2",
"date":"12/1/2014",
"invoiceId":2
},
{
"id":"3",
"date":"12/1/2014",
"invoiceId":3
}
]
}
Alternative Code
Use this if you don't need to make sure, invoiceId is there
var new_obj = {combined:[]};
obj["purchase"].forEach(function(a){a["invoiceId"]=a["id"];new_obj.combined.push(a);});
One way of achieving what you want will be
var result = {};
var getMatchingPurchase = function(invoiceId) {
return data.purchase.filter(function(purchase) {
return invoiceId == purchase.id;
})[0];
};
result.combined = data.item.map(function(invoice) {
var purchase = getMatchingPurchase(invoice.invoiceId);
return {
invoiceId: invoice.invoiceId,
id: purchase.id,
date: purchase.date
};
});
console.log(result);
It will print like bellow
{ combined:
[ { invoiceId: 1, id: '1', date: '12/1/2014' },
{ invoiceId: 2, id: '2', date: '12/1/2014' },
{ invoiceId: 3, id: '3', date: '12/1/2014' } ] }
Note:- I'm using map and filter functions which are not supported in IE8. If you want to use in IE8 you have to use for loop.
If you have to support old browsers like IE8 (poor guy...), note that the native forEach might not be supported, in this case you can use lodash for cross-browser compatibility:
function getCombinedResult(source){
var combinedList = [];
_.each(source.item, function(item){
_.each(source.purchase, function(purchase){
if (item['invoiceId'].toString() != purchase['id'].toString()) return;
var combinedItem = _.extend(item, purchase)
combinedList.push(combinedItem);
});
})
return {"combined": combinedList};
}
Related
For one of my e-commerce application requirement, I have a nested array of the form (Sample):
const data = [
{
"id": 1,
"group": "upper-wear",
"labels": [
{
"type": "shirts",
"quantity": "20",
},
],
popular: true
},
{
"id": 2,
"group": "bottom-wear",
"lables": [
{
"type": "trousers",
"quantity": "31",
},
],
popular: true
},
]
To this array, I need to insert new objects to the array 'labels' if the group value equals 'upper-wear'.
const newDataToInsert = [
{
"type": 'blazers',
"quantity": 19
},
]
This is what I tried so far, considering that for now I only need to insert to single label (i.e. 'upper-wear') (in future, there can be multiple labels category 'upper-wear', 'bottom-wear', to be inserted into):
const updatedArray = data.map((datum) => {
if (datum.group === 'upper-wear') {
return {
...datum,
labels: [...datum.labels, ...newDataToInsert]
};
}
});
console.log(updatedArray);
But there seems to be a silly issue that I am missing as the result returns like this:
[
{
id: 1,
group: 'upper-wear',
labels: [ [Object], [Object] ],
popular: true
},
undefined
]
I know there may be better approaches available, but this is what I can think of as the minimum solution for now.
any help to resolve the current or any better solution will be highly appreciated.
Try with this
updatedArray = data.map((d) => {
if (d.group && d.group === 'upper-wear') {
return { ...d, labels: d.labels.concat(newDataToInsert) }
} else {
return d;
}
})
const data = [
{
"id": 1,
"group": "upper-wear",
"labels": [
{
"type": "shirts",
"quantity": "20",
},
],
popular: true
},
{
"id": 2,
"group": "bottom-wear",
"lables": [
{
"type": "trousers",
"quantity": "31",
},
],
popular: true
},
];
const newDataToInsert = [
{
"type": 'blazers',
"quantity": 19
},
];
const updatedArray = data.map((d) => {
if (d.group && d.group === 'upper-wear') {
return { ...d, labels: d.labels.concat(newDataToInsert) }
} else {
return d;
}
});
console.log(updatedArray)
Explaination
Here while mapping the data, we check for the condition
IF
If it matches then we will first copy the whole object from the variable b return { ...b }
after that we take another variable with the same name lables return { ...d, labels: d.labels.concat(newDataToInsert) },As per the JSON default nature the new variable with the same name will hold the latest value
Here in labels we first take a copy of old data and then merge it with newDataToInsert array labels: d.labels.concat(newDataToInsert), It will merge 2 arrays and store them in JSON with the name labels
Else
In else we just return the current values else { return d; }
You don't actually need to iterate with map over the array. Just find an object in the array and change what you want.
const data=[{id:1,group:"upper-wear",labels:[{type:"shirts",quantity:"20"}],popular:true},{id:2,group:"bottom-wear",lables:[{type:"trousers",quantity:"31"}],popular:true}];
const newDataToInsert=[{type:"blazers",quantity:19}];
data.find(({ group }) => group === 'upper-wear')?.labels.push(...newDataToInsert);
console.log(data);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You're not returning all objects from your map. you're only returning a result when your criteria is met. This is resulting in your undefined objects...
const data = [
{ "id": 1, "group": "upper-wear", "labels": [ { "type": "shirts", "quantity": "20", }, ], popular: true },
{ "id": 2, "group": "bottom-wear", "lables": [ { "type": "trousers", "quantity": "31", }, ], popular: true },
]
const newDataToInsert = [ { "type": 'blazers',"quantity": 19 }, ]
const updatedArray = data.map(datum => {
if (datum.group === 'upper-wear') datum.labels = [...datum.labels, ...newDataToInsert]
return datum
});
console.log(updatedArray);
You can use Array#find to locate the desired group and then change labels for the group found. There are two options depending on how many items you would like to insert. Use Array#push to add the desired item; use forEach for more than one item:
const searchgroup = "upper-wear";
const target = data.find(({group}) => group === searchgroup);
target.labels.push(...newDataToInsert); //For one item to insert
//newDataToInsert.forEach(label => target.labels.push( label )); //For more than one item
const data = [{"id": 1, "group": "upper-wear", "labels": [{"type": "shirts", "quantity": "20"},],popular: true }, {"id": 2, "group": "bottom-wear", "lables": [{"type": "trousers", "quantity": "31", },],popular: true}];
const newDataToInsert = [{"type": 'blazers', "quantity": 19}];
//group to find
const searchgroup = "upper-wear";
//target element in data
const target = data.find(({group}) => group === searchgroup);
//check if group was found
if( target ) {
//if there's only one product in newDataToInsert us this:
//target.labels.push(...newDataToInsert);
//if you have more than one product to be inserted use this; also works for one
newDataToInsert.forEach(label => target.labels.push( label ));
} else {
console.log( `No such group found: ${searchgroup}!` );
}
console.log( data );
there is a list of users
filterData = [
{
"position":"lawyer",
"department_positions":[],
"group_positions":[
{"group":{"id":2,"code":"234","name":"group1"},"lead":false},
{"group":{"id":1,"code":"123","name":"group12"},"lead":true}
]
},
{
"position":"director",
"department_positions":[
{"department":{"id":3,"code":"333","name":"subDep"},"lead":false}
],
"group_positions":[
{"group":{"id":2,"code":"234","name":"group1"},"lead":false},
{"group":{"id":1,"code":"123","name":"group12"},"lead":true}
]
},
{
"position":"director",
"department_positions":[],
"group_positions":[]
}
]
and list of filters
categories = {
"position":["lawyer","director"],
"group_positions":["group1","group12"],
"department_positions":["generalDep", "subDep"]
}
It is necessary to filter users taking into account the fact that several filters can be selected at the same time. For example, i want to find user with position = "director" and AND group_positions = "group1" AND department_positions = "subDep"
my code doesn't allow filtering by multiple conditions. how can i fix it?
this.filter = this.filterData.filter(item => {
for (let key in this.categories) {
if (item[key].find(el =>
this.categories[key].includes(
el.group?.name || el.department?.name
)
)) {
return true
}
}
return false
})}
This is a good place to employ an es6 class to give behavior to the object being filtered. Augment each object to determine if it matches the "category" object.
(from the example data, this assumes the OP is looking for a "product of sums" match: for all of the category keys match at least one of the category values)
class FilterMe {
constructor(item) {
Object.assign(this, item);
}
namesForKey(key) {
switch (key) {
case 'position':
return [this.position]; // always answer an array
case 'group_positions':
return this.group_positions.map(gp => gp.group.name);
case 'department_positions':
return this.department_positions.map(dp => dp.department.name);
default:
return [];
}
}
// return true if a single filter key-value pair is matched
matchesFilterKeyValue(filterKey, filterOptions) {
const myNames = this.namesForKey(filterKey);
const matches = filterOptions.filter(e => myNames.includes(e));
return matches.length > 0;
}
// return true if all filter key-values pairs are matched
matchesFilter(filter) {
return Object.entries(filter).every(keyValue => {
return this.matchesFilterKeyValue(...keyValue);
})
}
}
const filterData = [{
"position": "lawyer",
"department_positions": [],
"group_positions": [{
"group": {
"id": 2,
"code": "234",
"name": "group1"
},
"lead": false
}, {
"group": {
"id": 1,
"code": "123",
"name": "group12"
},
"lead": true
}]
},
{
"position": "director",
"department_positions": [{
"department": {
"id": 3,
"code": "333",
"name": "subDep"
},
"lead": false
}],
"group_positions": [{
"group": {
"id": 2,
"code": "234",
"name": "group1"
},
"lead": false
}, {
"group": {
"id": 1,
"code": "123",
"name": "group12"
},
"lead": true
}]
},
{
"position": "director",
"department_positions": [],
"group_positions": []
}
]
const categories = {
"position": ["lawyer", "director"],
"group_positions": ["group1", "group12"],
"department_positions": ["generalDep", "subDep"]
}
// convert the filterData to the objects and test them...
let objects = filterData.map(d => new FilterMe(d));
let matches = objects.filter(o => o.matchesFilter(categories))
console.log(matches)
You can try something like this:
let filtered = example.filter(item => {
let valid = false
if (item.includes('something')) {
valid = true
}
if (!valid) {
// check second condition
}
return valid
})
Use a temporary placeholder so you don't immediately have to return true/false.
I'd like to map this table's chapter_id and brother_id with the brothers and chapters table below and return the brothername and name field's respectively. Using js or jquery. I am using vuejs returning minutes array as a computed property. See below.
In sql it's be something like
select brothername from brothers where minute.brother_id = brothers.id ... and then set the brothername as the new value for brother_id
same thing goes for chapter_id:
select brothername from brothers where minute.brother_id = brothers.id ... and then set the brothername as the new value for brother_id
the resulting array or object should be:
Expected Array
[
{
"location":"UCLA",
"chapter_id":"Beta",
"brother_id":"Golpher",
"created_at":"2008-05-15 22:23:00",
"status":"Approved"
},
{ ... },
{
"location":"John's Deli",
"chapter_id":"Beta", notice the change in the array based on the ids
"brother_id":"Sheera", notice the change in the array based on the ids
"created_at":"2008-05-15 22:23:00",
"status":"Approved"
}
]
Minutes Table (original array)
[
{
"location":"UCLA",
"chapter_id":2,
"brother_id":1,
"created_at":"2008-05-15 22:23:00",
"status":"Approved"
},
{ ... },
{
"location":"John's Deli",
"chapter_id":2,
"brother_id":4,
"created_at":"2008-05-15 22:23:00",
"status":"Approved"
}
]
Chapter's Table
[
{
"id":1,
"letter_representation":"A",
"name":"Alpha",
"founded_at":"UCLA",
...
},
{ ... }
]
Brother's Table
[
{
"id":1,
"profile_id":1,
"chapter_id":1,
"brothername":"Golpher",
"firstname":"Jack",
...
},
{ ... },
{
"id":4,
"profile_id":4,
"chapter_id":1,
"brothername":"Sheera",
"firstname":"Jake",
...
}
]
Vue.js
computed: {
brothers () {
return this.$store.state.brothers
},
chapters () {
return this.$store.state.chapters
},
minutes () {
return this.$store.getters.model
}
},
I assume that you don't want to mutate objects in the original arrays with this operation.
Note You may want to handle the case where brother_id or chapter_id doesn't exist in the corresponding table. In the below example, it just sets the property value to undefined
const minutesTable = [{
"location": "UCLA",
"chapter_id": 2,
"brother_id": 1,
"created_at": "2008-05-15 22:23:00",
"status": "Approved"
}, {
"location": "John's Deli",
"chapter_id": 2,
"brother_id": 4,
"created_at": "2008-05-15 22:23:00",
"status": "Approved"
}]
const chapterTable = [{
"id": 1,
"letter_representation": "A",
"name": "Alpha",
"founded_at": "UCLA",
}]
const brotherTable = [{
"id": 1,
"profile_id": 1,
"chapter_id": 1,
"brothername": "Golpher",
"firstname": "Jack",
}, {
"id": 4,
"profile_id": 4,
"chapter_id": 1,
"brothername": "Sheera",
"firstname": "Jake",
}]
// your result
const result = minutesTable.map(m => {
const brother = brotherTable.find(b => b.id === m.brother_id)
const chapter = chapterTable.find(c => c.id === m.chapter_id)
return Object.assign({}, m, {
brother_id: brother && brother.brothername,
chapter_id: chapter && chapter.name,
})
})
console.log(result)
This should be what you need
const minutesTable = [
{
"location":"UCLA",
"chapter_id":2,
"brother_id":1,
"created_at":"2008-05-15 22:23:00",
"status":"Approved"
},
{
"location":"John's Deli",
"chapter_id":2,
"brother_id":4,
"created_at":"2008-05-15 22:23:00",
"status":"Approved"
}
]
const chapterTable =
[
{
"id":1,
"letter_representation":"A",
"name":"Alpha",
"founded_at":"UCLA",
}
]
const brotherTable = [
{
"id":1,
"profile_id":1,
"chapter_id":1,
"brothername":"Golpher",
"firstname":"Jack",
},
{
"id":4,
"profile_id":4,
"chapter_id":1,
"brothername":"Sheera",
"firstname":"Jake",
}
]
/* code starts here */
let newMinutesTable = JSON.parse(JSON.stringify(minutesTable)).map(a => {
let brother = brotherTable.find(id => id.id === a.brother_id);
let chapter = chapterTable.find(id => id.id === a.chapter_id)
if (brother) a.brother_id = brother.brothername
if (chapter) a.chapter_id = chapter.name;
return a;
})
console.log([minutesTable,newMinutesTable]);
I think you should prepare those values first just to better understanding. So I made this, let me explain in pieces.
Your input information:
var minutesTable = [{
"location": "UCLA",
"chapter_id": 2,
"brother_id": 1,
"created_at": "2008-05-15 22:23:00",
"status": "Approved"
}, {
"location": "John's Deli",
"chapter_id": 2,
"brother_id": 4,
"created_at": "2008-05-15 22:23:00",
"status": "Approved"
}],
chapterTable = [{
"id": 1,
"letter_representation": "A",
"name": "Alpha",
"founded_at": "UCLA",
}],
brotherTable = [{
"id": 1,
"profile_id": 1,
"chapter_id": 1,
"brothername": "Golpher",
"firstname": "Jack",
}, {
"id": 4,
"profile_id": 4,
"chapter_id": 1,
"brothername": "Sheera",
"firstname": "Jake",
}];
Somehow you'll be forced to take this information as variables. We will work with that.
Preparing data
Dealing with array of objects it's a litle bit complicated when you need to look for unique informations on each object from distinct arrays especially if you want to run this more than once. So instead of working with arrays of objects we can save our lifes changing that to objects of objects, where each item index must be that unique IDs. Look:
var chapters = {},
brothers = {};
chapterTable.map(function(el, i) {
chapters[el.id] = el;
});
brotherTable.map(function(el, i) {
brothers[el.id] = el;
});
Now you can easily find a chapter by chapter_id or a brother by brother_id, right? Then you can finish the problem like this:
var output = [];
minutesTable.map(function(el, i) {
var item = {
"location": el.location, // note that values are just default values, just in case
"chapter_id":"-",
"brother_id":"-",
"created_at": el.created_at,
"status": el.status
};
// PS: you need to check if that brother_id really exists!
if(brothers[el.brother_id] != undefined) {
item.brother_id = brothers[el.brother_id].brothername;
}
// PS: the same with chapters
if(chapters[el.chapter_id] != undefined) {
item.chapter_id = chapters[el.chapter_id].name;
}
output.push(item);
});
That's it. Anyway, if you can change your SQL queries, would be better to work with SQL joins and prepare your values there.
I have an object like this:
{
"id": 23,
"name": "Jacob",
"link": {
"rel": "self",
"link": "www.abc.com"
},
"company":{
"data":{
"id": 1,
"ref": 324
}
}
I want to store each key with its value to an array in javascript or typescript like this
[["id":23], ["name":"Jacob"], ["link":{......, ......}]] and so on
I am doing this so that I can append an ID for each.
My best guess I would loop through the array and append an ID/a flag for each element, which I don't know how to do as well.... how to address this issue ? thanks
var arr = [];
for (var prop in obj) {
if (obj.hasOwnProperty(prop)) {
var innerObj = {};
innerObj[prop] = obj[prop];
arr.push(innerObj)
}
}
console.log(arr);
here is demo https://plnkr.co/edit/9PxisCVrhxlurHJYyeIB?p=preview
p.forEach( function (country) {
country.forEach( function (entry) {
entry.push( {"value" : 'Greece', "synonyms" : 'GR'});
});
});
you can try to use experimental Object.entries:
let obj = {
"id": 23,
"name": "Jacob",
"link": {
"rel": "self",
"link": "www.abc.com"
},
"company":{
"data":{
"id": 1,
"ref": 324
}
}};
console.log(Object.entries(obj).map(item => ({[item[0]]:item[1]})));
for unsupported browsers you can use polyfill: https://github.com/es-shims/Object.entries
You could use an iterative/recursive approach with the object and their nested parts. It works for any depths.
function getKeyValue(object) {
return Object.keys(object).reduce(function (result, key) {
return result.concat(
object[key] && typeof object[key] === 'object' ?
getKeyValue(object[key]) :
[[key, object[key]]]
);
}, []);
}
var data = { id: 23, name: "Jacob", link: { rel: "self", link: "www.abc.com" }, company: { data: { id: 1, ref: 324 } } };
console.log(getKeyValue(data));
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can use the Object.keys method to get an array of the keys, then use the Array#map method to return a new array containing individual objects for each property.
This ES6 one-liner should do it:
const splitObject = o => Object.keys(o).map(e => ({ [e]: o[e] }));
Or in ES5:
function splitObject(o) {
return Object.keys(o).map(function(e) {
return Object.defineProperty({}, e, {
value: o[e],
enumerable: true
});
});
}
var res = [];
_.transform( {
"id": 23,
"name": "Jacob",
"link": {
"rel": "self",
"link": "www.abc.com"
},
"company": {
"data": {
"id": 1,
"ref": 324
}
}
}, function(result, value, key) {
res.push(key +':'+value);
}, {});
You can use underscore
Supported in all major browser, including IE11
Object.entries() gives you exactly this.
const obj = {
id: 23,
name: 'Jacob',
link: {
rel: 'self',
link: 'www.abc.com'
},
company: {
data: {
id: 1,
ref: 324
}
}
};
Object.entries(obj);
// output:
[
[
"id",
23
],
[
"name",
"Jacob"
],
[
"link",
{
"rel": "self",
"link": "www.abc.com"
}
],
[
"company",
{
"data": {
"id": 1,
"ref": 324
}
}
]
]
var obj=[{"Name":ABC,"Count":123},{"Name":XYZ,"Count":456}];
var arr = [];
for (var prop in obj) {
if (obj.hasOwnProperty(prop)) {
var innerObj = {};
innerObj[0] = obj[prop];
arr.push(innerObj[0]);
}
}
/* Here above exmple innerobj index set to 0 then we will get same data into arr if u not menstion then arr will conatins arr[0] our result.
then we need to call first record obj arr[0][0] like this*/
const foo = { "bar": "foobar", "foo": "foobar" }
Object.entries(foo)
should result in:
[["bar", "foobar"], ["foo", "foobar"]]
maybe there's a function to pass to convert all commas to colons
Here's the documentation
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries
EDIT:
I believe this is a different problem than a simple _.groupBy because I am trying to group by all keys and returning an hashmap not an array.
I have an array of the following objects:
items = [
{ created_at: "01/01/2016", name: "bob", age: 21, height: 60 },
{ created_at: "01/02/2016", age: 22, height: 70 },
{ created_at: "01/03/2016", name: "alice", age: 23 }
]
And I am trying to transform it into this format:
{
"name": [
{
"value": "bob",
"created_at": "01/01/2016"
},
{
"value": "alice",
"created_at": "01/03/2016"
}
],
"age": [
{
"value": 21,
"created_at": "01/01/2016"
},
{
"value": 22,
"created_at": "01/02/2016"
},
{
"value": 23,
"created_at": "01/03/2016"
}
],
"height": [
{
"value": 60,
"created_at": "01/01/2016"
},
{
"value": 70,
"created_at": "01/02/2016"
}
]
}
My requirement is I am ignoring the created_at field, but grouping everything else.
One solution is:
var items = [
{ created_at: "01/01/2016", name: "bob", age: 21, height: 60 },
{ created_at: "01/02/2016", age: 22, height: 70 },
{ created_at: "01/03/2016", name: "alice", age: 23 }
]
var store = {}
for(var i=0; i < items.length; i++) {
var item = items[i]
for(key in item) {
if(key === "created_at") {
continue
}
value = item[key]
if(!store[key]) {
store[key] = []
}
store[key].push({
value: value,
created_at: item.created_at
})
}
}
$('pre').text(JSON.stringify(store, null, 2))
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<body><pre></pre></body>
I was wondering if there was some cool way to do it in lodash ... maybe using .map() or .groupBy() or something? I don't quite get how to use them properly and am trying to learn new, more elegant ways to write the code.
You asked specifically about lodash, and so this answer is constructed using lodash.
Note I'm using the _.chain method - this makes it convenient to chain multiple operations rather than running them each separately.
One way (I'm sure there are many others) that gets it pretty close (not perfect) is this:
items = _.chain(items)
.map(function(n) {
var created = _.get(n, 'created_at');
var arr = [];
_.each(n, function(n, k) {
if (k != 'created_at') {
arr.push({
'field': k,
'value': n,
'created_at': created
});
}
});
return arr;
})
.flatten()
.groupBy('field')
.value();
Which results in a collection that looks like this:
{ "name":
[
{ "field": "name", "value": "bob", "created_at": "01/01/2016" },
{ "field": "name", "value": "alice", "created_at": "01/03/2016" }
],
"age": [
....
Here's a Fiddle for playing around with this code.
Using reduce might be the shortest way.
var baseKey = 'created_at';
var result = items.reduce(function(col, item) {
Object.keys(item).forEach(function(key) {
if (key === baseKey || !item[key]) return;
var o = { value: item[key] };
o[baseKey] = item[baseKey];
(col[key] = col[key] || []).push(o);
});
return col;
}, {});
JSFiddle Demo: https://jsfiddle.net/pLcv1am2/6/