How can I inner join with two object arrays in JavaScript? - javascript

I need inner join with two array in javascript like this:
array1 =
[
{
"id": 1,
"name": "Tufan"
},
{
"id": 2,
"name": "Batuhan"
},
{
"id": 3,
"name": "Hasan"
}
]
array2 =
[
{
"name": "yyy",
"externalid": "1",
"value": "Asd"
},
{
"name": "aaaa"
"externalid": "2",
"value": "ttt"
}
]
expectedArray =
[
{
"id": 1,
"name": "Tufan",
"externalid": "1",
"value": "Asd"
},
{
"id": 2,
"name": "Batuhan",
"externalid": "2",
"value": "ttt"
}
]
rules:
on: array2.externalid = array1.id
select: array1.id, array1.name, array2.externalid, array2.value
My approach:
array1.filter(e => array2.some(f => f.externalid == e.id));
// I need help for continue
How can I make this?
Doesn't matter information: I use ES5 and pure javascript

You can do it like this:
const res = array2.map((item) => {
const related = array1.find((el) => el.id == item.externalid);
return { ...item, ...related };
});
Using a map to loop over the array2 and a find to get the array1 relative.

Related

copying data from one array to another array

Array One:
array1 = [{
"id": 1,
"name": "aaaaa",
"attr": [{"attr_code": "a_id", "value": "5"}]
},
{
"id": 2,
"name": "bbbbb",
"attr": [{"attr": "a_id", "value": "4"}]
}]
Array Two:
array2 = [{
"id": 4,
"name": "bef",
},
{
"id": 5,
"name": "bcd",
}]
Resulting Array:
resultingArray = [{
"id": 1,
"name": "aaaaa",
"attr": [{"attr_code": "a_id", "value": "5"}],
"a_id" : {"id": 5, "name": "bcd"}
},
{
"id": 2,
"name": "bbbbb",
"attr": [{"attr": "a_id", "value": "4"}],
"a_id" : {"id": 4, "name": "bef"}
}]
I am looking to add the array2 objects into array1 based on id's of array2. I have tried using map function on both the arrays to compare and add the object but I didn't succeed. Can you please suggest me how to do it?
Thank you
Add the array2 objects into array1 based on ids of array2.
let array1 =
[
{
"id": 1,
"name": "aaaaa",
"attr": [{"attr_code": "a_id", "value": "5"}]
},
{
"id": 2,
"name": "bbbbb",
"attr": [{"attr": "a_id", "value": "4"}]
}
];
let array2 = [{
"id": 4,
"name": "bef",
},
{
"id": 5,
"name": "bcd",
}
];
let resultingArray=[];
array1.forEach(function(element) {
element['a_id'] = [];
element['attr'].forEach(function(attr) {
element['a_id'].push(array2.find(function(item) {
return item.id == attr.value;
}));
});
resultingArray.push(element)
});
console.log(resultingArray);
I presume you intend to extract the object whose ID is equal to the value field the in the each object in array1.
var array1 = [{
"id": 1,
"name": "aaaaa",
"attr": [{"attr_code": "a_id", "value": "5"}]
},
{
"id": 2,
"name": "bbbbb",
"attr": [{"attr": "a_id", "value": "4"}]
}];
var array2 = [{
"id": 4,
"name": "bef",
},
{
"id": 5,
"name": "bcd",
}];
var resultingArray = [];
for(var i = 0; i < array1.length; i++) {
resultingArray[i] = array1[i];
for(var j = 0; j < array2.length; j++) {
if(resultingArray[i].attr[0].attr_code.value === array2[j].id) {
resultingArray[i].push("a_id": array2[j]);
}
}
}
You just need to lop through array1, and for each object in array1, you need to find corresponding objects in array2 which match the criterion.
You can use array map and array index to do:
var array1 = [{
"id": 1,
"name": "aaaaa",
"attr": [{"attr_code": "a_id", "value": "5"}]
},
{
"id": 2,
"name": "bbbbb",
"attr": [{"attr": "a_id", "value": "4"}]
}];
var array2 = [{
"id": 4,
"name": "bef",
},
{
"id": 5,
"name": "bcd",
}];
var result = array1.map(current=>{
//find index of attr in array2
let index = array2.findIndex(c=>{
if (c['id']===(Number(current['attr'][0]['value'])))
return c;
});
current["a_id"] = array2[index];
return current;
});
console.log(result);
Please check if the following code suites your requirement. You may need to make some changes.
function mergeArrays3 (arr1, arr2) {
return arr1.map((value, index) => {
let object = null;
let result = {...value};
for (let element of arr2) {
if (element.id == parseInt(value.attr[0].value)) {
object = element;
break;
}
}
if (object != null) {
let attr = value.attr[0];
if (attr.hasOwnProperty("attr")) {
result[value.attr[0].attr] = object;
} else if (attr.hasOwnProperty("attr_code")) {
result[value.attr[0].attr_code] = object;
}
}
return result;
});
}
I loop over first array and find an element in second array matching id of value.attr[0].value. If found then i added this object in the first array at key of value.attr[0].attr or value.attr[0].attr_code.
I have tried using map function on both the arrays to compare and add
the object but I didn't succeed
Below is the functional programming approach using map():
/* GIVEN */
const array1 = [{
"id": 1,
"name": "aaaaa",
"attr": [{
"attr_code": "a_id",
"value": "5"
}]
},
{
"id": 2,
"name": "bbbbb",
"attr": [{
"attr": "a_id",
"value": "4"
}]
}
]
const array2 = [{
"id": 4,
"name": "bef",
},
{
"id": 5,
"name": "bcd",
}]
/* From array2, make an object keyed by the 'id' field. We'll use this as a key-value lookup table */
const lookupTable = array2.reduce((accum, item) => {
accum[item.id.toString()] = item
return accum
}, {})
console.log('***LOOKUP TABLE***\n', lookupTable) // result is an object we use to lookup
/* From array1, we append data from the lookup table */
const final = array1.map(item => {
item.a_id = lookupTable[item.attr[0].value]
return item
})
console.log("***RESULT***\n", final)
Hope this helps.
Cheers,

Filtering an array based on multiple arrays inputs

I have three arrays.
1. Existing viewers array - existingViewers
New viewers array - newViewers
Permitted Viewers array - permittedViewers
permittedViewers is used for rendering the drop-down. And I wish to filter the newViewers and existingViewers entries from the permittedViewers.
I am doing this as three steps. And I am afraid this is not the optimized way. Can someone suggest the ideal way of doing this?
The expected result is
[
{
"id": 4,
"name": "name4"
},
{
"id": 5,
"name": "name5"
},
{
"id": 6,
"name": "name6"
}
]
let existingViewers = [{
"viewerId": 1,
"name": "name1"
},
{
"viewerId": 2,
"name": "name2"
}
],
newViewers = [
{
"viewerId": 3,
"name": "name3"
}
],
permittedViewers = [{
"id": 1,
"name": "name1"
},
{
"id": 2,
"name": "name2"
},
{
"id": 3,
"name": "name3"
},
{
"id": 4,
"name": "name4"
},
{
"id": 5,
"name": "name5"
},
{
"id": 6,
"name": "name6"
}
]
let grouped = [...existingViewers, ...newViewers]
let viewerFilter = grouped.map(viewer => { return viewer.viewerId; });
let filteredPermittedViewers = permittedViewers.filter(viewer => !viewerFilter.includes(viewer.id));
console.log(filteredPermittedViewers)
I'd make a Set of the ids of the first two arrays, and then filter the third by whether the set includes the id. (Sets have O(1) lookup time)
let existingViewers=[{"viewerId":1,"name":"name1"},{"viewerId":2,"name":"name2"}],newViewers=[{"viewerId":3,"name":"name3"}],permittedViewers=[{"id":1,"name":"name1"},{"id":2,"name":"name2"},{"id":3,"name":"name3"},{"id":4,"name":"name4"},{"id":5,"name":"name5"},{"id":6,"name":"name6"}];
const ids = new Set([...existingViewers, ...newViewers].map(({ viewerId }) => viewerId));
const output = permittedViewers.filter(({ id }) => !ids.has(id));
console.log(output);
You can compress all three statements into a single statement -- just replace the variable name with the statement that creates it:
let existingViewers = [{
"viewerId": 1,
"name": "name1"
},
{
"viewerId": 2,
"name": "name2"
}
],
newViewers = [
{
"viewerId": 3,
"name": "name3"
}
],
permittedViewers = [{
"id": 1,
"name": "name1"
},
{
"id": 2,
"name": "name2"
},
{
"id": 3,
"name": "name3"
},
{
"id": 4,
"name": "name4"
},
{
"id": 5,
"name": "name5"
},
{
"id": 6,
"name": "name6"
}
]
let filteredPermittedViewers = permittedViewers.filter(viewer => ! [...existingViewers, ...newViewers].map(viewer => viewer.viewerId).includes(viewer.id));
console.log(filteredPermittedViewers)

Javascript filter for multidimensional json object

Can't use javascript filter in multi-dimensional object.
var object = [{
"id": "1",
"name": "General",
"cards": [{
"id": "1",
"name": "shawn"
}, {
"id": "2",
"name": "neo"
}]
}, {
"id": "2",
"name": "CEO",
"cards": [{
"id": "1",
"name": "Raman"
}, {
"id": "2",
"name": "Sheena"
}]
}]
function searchFor(item) {
return item.cards.filter(
(card) => {
return card.name.indexOf("Raman") !== -1;
}
);
}
var filtered = object.filter(searchFor);
console.log(filtered);
This is how I am trying, inside the searchFor card.name I am getting the correct card name but filtering is returning all the cards.Its not filtering.
Could any help me with this.
An empty array isn't considered falsey in Javascript. So instead of returning the result of filtering the cards array, test its length.
var object = [{
"id": "1",
"name": "General",
"cards": [{
"id": "1",
"name": "shawn"
}, {
"id": "2",
"name": "neo"
}]
}, {
"id": "2",
"name": "CEO",
"cards": [{
"id": "1",
"name": "Raman"
}, {
"id": "2",
"name": "Sheena"
}]
}]
function searchFor(item) {
return item.cards.filter(
(card) => {
return card.name.indexOf("Raman") !== -1;
}
).length != 0;
}
var filtered = object.filter(searchFor);
console.log(filtered);
You were returning the filtered array, which would produce a TRUE result whenever cards existed. So you can just turn that into a boolean, by saying when the item.cards.filter(...).length > 0.
var object = [{
"id": "1",
"name": "General",
"cards": [{
"id": "1",
"name": "shawn"
}, {
"id": "2",
"name": "neo"
}]
}, {
"id": "2",
"name": "CEO",
"cards": [{
"id": "1",
"name": "Raman"
}, {
"id": "2",
"name": "Sheena"
}]
}]
var searchFor = (card) => card.name.indexOf("Raman") > -1;
var filteredCards = object.reduce((cards, item) => cards.concat(item.cards.filter(searchFor)), []);
var filteredObj = object.map(i => {
i.cards = i.cards.filter(searchFor);
return i;
}).filter(i => i.cards.length)
console.log(filteredCards, filteredObj)
Updated
I updated the code snippet to produce either the cards which were found. I also provide a method for returning all objects which contain the needed cards, and filter out the other cards.
// HTML Part
<div class="filter-list">
<button class="filter" data-filter-key="all">all</button>
<button class="filter" data-filter-key="open">open</button>
<button class="filter" data-filter-key="done">done</button>
</div>
// CSS Part
.filter:hover,
.filter:focus,
[data-active-filter="all"] .filter[data-filter-key="all"],
[data-active-filter="done"] .filter[data-filter-key="done"],
[data-active-filter="open"] .filter[data-filter-key="open"] {
text-decoration: underline;
}
[data-active-filter="open"] [data-completed="true"],
[data-active-filter="done"] [data-completed="false"] {
display: none;
}
// Script Part
(function () {
const mainNode = document.querySelector("main");
const filters = document.querySelector(".filter-list");
for (const filter of filters.children) {
filter.addEventListener("click", () => {
mainNode.setAttribute(
"data-active-filter",
filter.getAttribute("data-filter-key")
);
});
}
mainNode.setAttribute("data-active-filter", "all");
})();

Building new JSON from existing one

I want to build an new JSON from existing one. The source has sections and rubrics that I no longer need for a listing. The new object called 'items' should have an array of the items.
The final JSON should be sorted by attribute 'name' and look like
{
"items": [
{
"id": 10000006,
"name": "Boah"
},
{
"id": 10000013,
"name": "Gut"
},
{
"id": 10000003,
"name": "Ipsum"
},
{
"id": 10000001,
"name": "Lorem"
},
{
"id": 10000005,
"name": "Lorum"
},
{
"id": 10000004,
"name": "Name"
},
{
"id": 10000002,
"name": "Stet"
}
]
}
For building the new JSON I get this source:
{
"sections": [
{
"name": "FooBar",
"rubrics": [
{
"name": "Foo",
"items": [
{
"id": 10000001,
"name": "Lorem"
},
{
"id": 10000002,
"name": "Stet"
},
{
"id": 10000003,
"name": "Ipsum"
}
]
},
{
"name": "Bar",
"items": [
{
"id": 10000004,
"name": "Name"
},
{
"id": 10000005,
"name": "Lorum"
},
{
"id": 10000006,
"name": "Boah"
}
]
}
]
},
{
"name": "BlahBloob",
"rubrics": [
{
"name": "Bla",
"items": [
{
"id": 10000013,
"name": "Gut"
}
]
},
{
"name": "Bloob",
"items": [
{
"id": 10000014,
"name": "Name"
},
{
"id": 10000015,
"name": "Lorem"
}
]
}
]
}
]
}
What do you think? How can I do this with plain JavaScript or maybe TypeScript?
Thanks for reading and have time for my question. And thanks for reply in advance.
Here you go. You just need to iterate over each rubric of each section of your source to get the items. At the end, sort your list of items by items, and you're done.
This example uses ES6 syntax, but it's easy to convert it to ES5 if needed.
function extractItems(source) {
const items = [];
for (const section of source.sections) {
for (const rubric of section.rubrics) {
items.push(...rubric.items);
}
}
items.sort((a, b) => a.name.localeCompare(b.name));
return { items };
}
A more functional approach use map and reduce to pick the rubrics and merge them.
data.sections
.map(section => section.rubrics) // get rubrics
.reduce((a, b) => a.concat(b)) // merge rubrics
.map(rubric => rubric.items) // get items from each rubric
.reduce((a, b) => a.concat(b)) // merge items
.sort((a, b) => a.name.localeCompare(b.name)); // sort
function(oldObj) {
var newObj = {
"items": []
};
oldObj.sections.forEach(function(section) {
section.rubrics.forEach(function(rubric) {
rubric.items.forEach(function(item) {
newObj.items.push(item);
});
});
});
newObj.items = newObj.items.sort(function(a, b) {
if (a.name < b.name) { return -1; }
if (a.name > b.name) { return 1; }
return 0;
});
return newObj;
}
And simply use JSON.parse() and JSON.stringify() to convert JSON to and from objects.
It might help you
var data ={
"sections": [
{
"name": "FooBar",
"rubrics": [{"name": "Foo", "items": [{"id": 10000001,"name": "Lorem"}, {"id": 10000002,"name": "Stet"}, {"id": 10000003,"name": "Ipsum"}]
}, {
"name": "Bar",
"items": [{
"id": 10000004,
"name": "Name"
}, {
"id": 10000005,
"name": "Lorum"
}, {
"id": 10000006,
"name": "Boah"
}]
}]
}, {
"name": "BlahBloob",
"rubrics": [{
"name": "Bla",
"items": [{
"id": 10000013,
"name": "Gut"
}]
}, {
"name": "Bloob",
"items": [{
"id": 10000014,
"name": "Name"
}, {
"id": 10000015,
"name": "Lorem"
}]
}]
}]
};
var itemObj = {};
var itemArr = [];
var sections = data.sections;
for(var i=0;i<sections.length;i++)
{
for(var j=0;j<sections[i].rubrics.length;j++){
for(var k=0;k<sections[i].rubrics[j].items.length;k++){
var itemObj;
itemObj['id'] = sections[i].rubrics[j].items[k].id;
itemObj['name'] = sections[i].rubrics[j].items[k].name;
itemArr.push(itemObj);
}
}
}
var finalObj = {"items":itemArr};
console.log(finalObj);
JSFiddle

Nesting a parent child relationship in lodash, given the parent id and children

How would I be able to nest json object if the parent and its children was given as a property.
The data looks like:
"1": {
"id": 1,
"name": "foo",
"parent": null,
"root": 1,
"children": [2, 4, 6],
"posts":[
{ "id": "1", "name": "item1" },
{ "id": "2", "name": "item2" },
{ "id": "3", "name": "item3" }
]
},
"2": {
"id": 2,
"name": "bar",
"parent": 1,
"root": 1,
"children": null,
"posts":[
{ "id": "4", "name": "item4" }
]
},
"3": {
"id": 3,
"name": "bazz",
"parent": null,
"root": 3,
"children": [5, 7],
"posts":[
{ "id": "5", "name": "item5" },
{ "id": "6", "name": "item6" }
]
},
....
A simple groupby using lodash won't do it.
var group = _.groupBy(data, 'parent');
Here is a fiddle:
http://jsfiddle.net/tzugzo8a/1/
The context of question is a nested categories with subcategories, and categories can have categories and posts in them.
Basically I don't want to have a different property for children and posts, since they are all children of a parent.
Desired output
"1": {
"id": 1,
"name": "foo",
"parent": null,
"root": 1,
"isCategory": true,
"children": [
{
"id": 2,
"name": "bar",
"parent": 1,
"root": 1,
"isCategory": true,
"children": null
},
{ "id": "1", "name": "item1", isCategory: false },
{ "id": "2", "name": "item2", isCategory: false },
{ "id": "3", "name": "item3", isCategory: false }
]
...
}
This is my take on the question (fiddle):
var data = getData();
var group = getTree(data);
console.log(group);
function getTree(flat) {
return _.reduce(flat, function (treeObj, item, prop, flatTree) {
var children = _.map(item.children, function (childId) {
return _.set(flatTree[childId], 'isCategory', true);
}).concat(_.map(item.items, function(item) {
return _.set(item, 'isCategory', false);
}));
item.children = !!children.length ? children : null;
delete item.items;
item.parent === null && (treeObj[prop] = item);
return treeObj;
}, {});
}
Take a look on the updated fiddle:
var data = getData();
_.keys(data).forEach(function(id){
var element = data[id];
if (element.children === null){
element.children = [];
}
element.isCategory = true;
element.items.forEach(function(item){
item.isCategory = false;
})
});
_.keys(data).forEach(function(id){
var element = data[id];
element.children = element.children.map(function(childId){
return data[childId];
}).concat(element.items);
});
_.keys(data).forEach(function(id){
delete data[id].items;
});
console.log(JSON.stringify(_.findWhere(_.values(data), {'parent': null})));

Categories

Resources