merge array object within array based on same id - javascript

Hello I have a 2 variable with object and with a value of array object, in "test" and "test_2" i would like to merge the sku ("test") and feature ("test_2) based on id (as per below). What is the best way to merge, Im thinking of, for the test_2 I convert the feature into value array object ex: feature:[{id:xxx}] first and after that I do the merge, as I try it doesn't work. Hope anyone here can help me. Thank you
const test =
{
sku: [
{id:"282828282", link:"ksksks.com"},
{id:"676867868", link:"lallaa.com"},
{id:"543454554", link:"sssss.com"},
{id:"345473663", link:"fdfsas.com"}
],
}
const test_2 =
{
panels: [
{id:"9328492847", feature: ['282828282']},
{id:"6756734535", feature: ['543454554', '282828282']},
{id:"6545353453", feature: []},
{id:"4353567688", feature: []},
]
}
const test_3 =
{
panels: [
{id:"9328492847", feature: [{id: '282828282', link:"ksksks.com"} ]},
{id:"6756734535", feature: [{id: '543454554', link:"sssss.com"}, {id:'282828282',link:"ksksks.com"}]},
{id:"6545353453", feature: []},
{id:"4353567688", feature: []},
]
}

let skuSet = {};
for (let i = 0; i < test.sku.length; i++) {
if (!skuSet[test.sku[i].id]) {
skuSet[test.sku[i].id] = test.sku[i];
}
}
for (let i = 0; i < test_2.panels.length; i++) {
let current = test_2.panels[i];
for (let j = 0; j < current.feature.length; j++) {
if (skuSet[current.feature[j]]){
current.feature[j] = skuSet[current.feature[j]];
}
}
}
console.log(JSON.stringify(test_2));
Let me know if you have any question regarding above logic.

let skuMap = {};
test.sku.map((skuData) => { skuMap[skuData.id] = skuData })
test_2.panels.map((panelData) => {
panelData.feature = panelData.feature.map((panelFeatureId) => skuMap[panelFeatureId])
})
Simple and fast

Related

Creating new object from 2 existing objects

I have the following:
var identificationIDs = [
{
"HolderID": "1A000714",
"TempIssueID": "1A000700"
}
]
Which I am trying to update to include a new object "ExtendedID" with some values to look like below:
var identificationIDs = [
{
"HolderID": "1A000714",
"TempIssueID": "1A000700",
"ExtendedID": [
"1A000714",
"1A000700"
]
}
]
Running into issues with trying to push HolderID and TempIssueID into the new object.
Here is my code:
// Simplify variable name:
var userID = identificationIDs;
// Create new object and assign values:
for (var i = 0; i < userID.length; i++) {
userID[i].HolderID = userID[i].ID;
userID[i].ExtendedID = userID[i].HolderID.push(TempIssueID);
}
console.log(userID);
You can use Javascript's built-in spread syntax to help you out.
If you're playing around with arrays, minor changes should be made. Take a look at an example:
let identificationIDs = {
"HolderID": "1A000714",
"TempIssueID": "1A000700"
}
let extendedId = [
"1A000714",
"1A000700"
]
let newIdentificationIds = {...identificationIDs, ExtendedID: extendedId};
console.log(newIdentificationIds)
You can try these following ways.
var identificationIDs = [
{
HolderID: "1A000714",
TempIssueID: "1A000700",
},
];
// Using `Object.values`
const result = identificationIDs.map((obj) => ({
...obj,
ExtendedID: Object.values(obj),
}));
// Alternatively, use destructuring
const result2 = identificationIDs.map(({ HolderID, TempIssueID }) => ({
HolderID,
TempIssueID,
ExtendedID: [HolderID, TempIssueID],
}));
console.log(result);
console.log(result2);

Filter Array of objects into seperate lists

So to give an example = >
Object:
WachtRegistratieID: (...)
Name: (...)
StartDateTime: (...)
StopDateTime: (...)
PersonName: (...)
Description: (...)
Phone1: (...)
Phone2: (...)
Phone3: (...)
Phone4: (...)
Phone5: (...)
Phone6: (...)
This is 1 object, What I'm trying to do is this =>
in an array, I can have separate objects with the same name, but all the rest are different date, now I want to split 1 big array into separate arrays for those who have the same name, something like this =>
Array[[Object{Name=first},Object{Name=first}],[Object{Name=second},Object{Name=second}]]
how i'm currently trying to it is like this =>
export function CompileList(list) {
let compiledList = [];
for (let i = 0; i < list.length; i++) {
if (i === 0) {
compiledList.push([list[i]]);
}
for (let j = 0; j < compiledList; j++) {
console.log(compiledList[j][0].Name + " and this other " + list[i].Name);
if (compiledList[j][0].Name == list[i].Name) {
compiledList[j].push = list[i];
} else {
compiledList.push(list[i]);
}
}
}
return compiledList;
}
the output of this is correct, but the problem is here for every name there is an extra array made like this =>
Array[[Object{Name=first},Object{Name=first}],[Object{Name=first},Object{Name=first}]] because I have two times the same name...
sorry if this is a duplicate question or if it is not clear what I mean, let me know
if you are looking for grouping array of objects by key, I made a small snippet for you.
const list = [
{name: "abc", "phone1": 12345678, "phone2": 98789656},
{name: "def", "phone1": 34567098, "phone2": 12345667},
{name: "abc", "phone1": 234567098,"phone2": 124455667},
]
var compiledList = list.reduce((acc, x) => {
if(!acc[x.name]) {
acc[x.name] = []
}
acc[x.name].push(x);
return acc;
}, {});
console.log(compiledList);

I want to pull the names with the top 5 values out of an object...in React

I have my state set to an empty object. When something is searched it returns a list of names with values inside of that object. How do I pull the names with the top 5 values so only those names render?
Here is my initial state:
export default class App extends React.Component {
constructor() {
super();
this.state = {
searchInput: '',
selectedArtist: {},
selectAlbums: {},
artistCounts: {},
};
Here is the fucntion that gets the names and gives them a value:
getTrackDetails(track) {
request.get(`api`)
.then((track) => {
const { artists } = track.body;
const artistCounts = this.state.artistCounts;
for (let i = 0; i < artists.length; i++) {
const artist = artists[i];
const artistName = artist.name;
if (artistCounts[artistName]) {
artistCounts[artistName] += 1;
}
else {
artistCounts[artistName] = 1;
}
}
Not sure am I getting this right but it looks like you need to limit this
for (let i = 0; i < artists.length; i++) {
to run up to 5 times so:
var maxLength = artists.length >= 5 ? 5 : artists.length;
for (let i = 0; i < maxLength ; i++) {
Put them into array, sort the array, the slice the top:
// Example, in your case you have this after the for loop
var artistCounts = {};
artistCounts["beyonce"] = 135;
artistCounts["rihanna"] = 67;
artistCounts["taylor swift"] = 45;
artistCounts["drake"] = 199;
// Create array of objects
var t = [];
for (var k in artistCounts) {
if (artistCounts.hasOwnProperty(k)) {
t.push({"name": k, "count": artistCounts[k]});
}
}
// Sort by count, descending
t.sort(function(a, b) {
return a.count < b.count
});
t.slice(0, 5); // Your top 5
You can use lodash for your purposes. It's a great library for data manipulation. Here is an example with explanation:
const { artists } = track.body;
// Let's imagine data looks like [{name: 'A'}, {name: 'B'}, {name: 'A'}, {name: 'A'}, {name: 'B'}, {name: 'C'}, {name: 'D'}, {name: 'D'}, {name: 'B'}];
this.state.artistCounts = _
.chain(artists)
.countBy('name') // {"A":3,"B":3,"C":1,"D":2}
.toPairs() // [["A",3],["B",3],["C",1],["D",2]]
.sortBy(1) // [["C",1],["D",2],["A",3],["B",3]]
.reverse() // [["B",3],["A",3],["D",2],["C",1]]
.slice(0, 5) // Get top 5
.fromPairs() // Zip array back to object
.value(); // { "B": 3, "A": 3, "D": 2, "C": 1 }
Assuming your artists array is in shape of `[{name: "artistName1"}, {name: "artistName2"}], I would convert it into an map which holds artist names as key and number of occurence in value. You can convert your array to this map using a reduce action:
artistsPerCount = artists.reduce(function(artistMap = {}, currentArtist) {
artistMap[currentArtist.name] = artistMap[currentArtist.name] || {
count: 0
};
artistMap[currentArtist.name].count++;
return artistMap;
}, this.state.artistCounts);
And after having this map, you can use the following function to return top n artists:
function returnTopNArtist(artistMap = {}, n) {
const artistsArray = Object.keys(artistMap).map(artistName => ({
name: artistName,
count: artistMap[artistName].count
}));
return artistsArray.sort(function(a1, a2){
return a2.count - a1.count;
}).slice(0, n);
}
const topNArtists = returnTopNArtist(artistsPerCount, 5); //returns top 5 artist
So in your case you can have the following implementation assuming returnTopNArtist is a method of your component.
getTrackDetails(track) {
request.get(`api`)
.then((track) => {
const { artists } = track.body;
const artistCounts = this.state.artistCounts;
artistCounts = artists.reduce(function(artistMap = {}, currentArtist) {
artistMap[currentArtist.name] = artistMap[currentArtist.name] || {
count: 0
};
artistMap[currentArtist.name].count++;
return artistMap;
}, artistCounts);
const top5Artist = this.returnTopNArtist(artistCounts, 5);

Javascript Array to Object

I have an array that looks like so:
files = [
'Dashboard/Logs/Errors',
'Dashboard/Logs/Other',
'Accounts/Main',
]
I want to make it look like this:
navigation = [
{
"title": "Dashboard",
"dropdown": [
{
"title": "Logs",
"dropdown": [
{
"title": "Errors",
},
{
"title": "Other",
}
]
}
]
},
{
"title": "Accounts",
"dropdown": [
{
"title": "Main",
}
]
}
]
I have the following so far:
var navigation = [];
for (var i = 0; i < files.length; i++) {
var parts = files[i].split('/');
navigation.push({title: parts[0]});
for (var j = 1; j < parts.length; j++) {
}
}
I am having difficulties figuring out a decent way to do this. What I have so far already doesn't work because it creates two objects under navigation each with title: "Dashboard". Any ideas for a clever approach? Thanks :)
This should produce the desired output:
var files = [
'Dashboard/Logs/Errors',
'Dashboard/Logs/Other',
'Accounts/Main',
];
var navigation = [];
// Iterates through a navigation array and returns the object with matching title, if one exists.
var getNavigationObject = function(nav, title) {
for (var i = 0; i < nav.length; i++) {
if (nav[i].title == title) {
return nav[i];
}
}
};
// Adds a file to the nav.
// The input is an array of file components (i.e. file.split('/'))
// This works by recursively adding each component of a file.
var addToNav = function (nav, components) {
var n = getNavigationObject(nav, components[0]);
if (!n) {
n = {
title: components[0]
};
nav.push(n);
}
if (components.length > 1) {
n.dropdown = n.dropdown || [];
addToNav(n.dropdown, components.slice(1));
}
};
// Actually call `addToNav` on each file.
files.forEach(function(e) {
addToNav(navigation, e.split('/'));
});
// Produces the result in string form.
JSON.stringify(navigation, null, 2)
This works by recursively checking if a given element already matches the component of the file. If it does, it recurs into that component's "dropdown". Otherwise, it creates it.
This is an approach with a temporary object and some array methods with no search overhead.
var files = ['Dashboard/Logs/Errors', 'Dashboard/Logs/Other', 'Accounts/Main'],
navigation = function (data) {
var r = [], o = {};
data.forEach(function (a) {
var s = r;
a.split('/').reduce(function (p, b) {
if (p.children) {
p.value.dropdown = p.value.dropdown || [];
s = p.value.dropdown;
p = p.children;
}
if (!(b in p)) {
p[b] = { value: { title: b }, children: {} };
s.push(p[b].value);
}
return p[b];
}, o);
});
return r;
}(files);
document.write('<pre>' + JSON.stringify(navigation, 0, 4) + '</pre>');

javascript find child object in nested arrays

I have a javascript structure like below (nested arrays of objects)
var categoryGroups = [
{
Id: 1, Categories: [
{ Id: 1 },
{ Id: 2 },
]
},
{
Id: 2, Categories: [
{ Id: 100 },
{ Id: 200 },
]
}
]
I want to find a child Category object matching an Id, assuming the Category Id's are all unique.
I've got this below, but was wondering if there is a more concise way of doing it:
var category, categoryGroup, found = false;
for (i = 0; i < categoryGroups.length ; i++) {
categoryGroup = categoryGroups[i];
for (j = 0; j < categoryGroup.Categories.length; j++) {
category = categoryGroup.Categories[j];
if (category.Id === id) {
found = true;
break;
}
}
if (found) break;
}
Using flatMap in ES2019
const category = categoryGroups.flatMap(cg => cg.Categories).find(c => c.Id === categoryId);
Caveat: This uses a couple of Array.prototype functions that were only added in ECMAScript 5 and thus will not work with older browsers unless you polyfill them.
You can loop over all first-level objects in your array, and then filter the categories based on your condition and collect all matches in an array. Your final result will be the first element in the array of matches (no match found if array is empty).
var matches = [];
var needle = 100; // what to look for
arr.forEach(function(e) {
matches = matches.concat(e.Categories.filter(function(c) {
return (c.Id === needle);
}));
});
console.log(matches[0] || "Not found");
JSFiddle: http://jsfiddle.net/b7ktf/1/
References:
Array.prototype.forEach
Array.prototype.concat
Array.prototype.filter
Using only Array.prototype.filter():
If you are sure that the id you are looking for exists, you can do:
var id = 200; // surely it exists
var category = arr.filter(g => g.Categories.filter(c => c.Id === id)[0])[0].Categories.filter(c => c.Id === id)[0];
If you are not sure that it exists:
var id = 201; // maybe it doesn't exist
var categoryGroup = arr.filter(e => e.Categories.filter(c => c.Id === id)[0])[0];
var category = categoryGroup ? categoryGroup.Categories.filter(c => c.Id === id)[0] : null;
jsfiddle
Using reduce and recursion :
function nestedSearch(value) {
return categoryGroups.reduce(function f(acc, val) {
return (val.Id === value) ? val :
(val.Categories && val.Categories.length) ? val.Categories.reduce(f, acc) : acc;
});
}
> try on JSFiddle
check the code in the fiddle
var categoryGroups = [
{
Id: 1, Categories: [
{ Id: 1 },
{ Id: 2 },
]
},
{
Id: 2, Categories: [
{ Id: 100 },
{ Id: 200 },
]
}
]
var id = 100;
var x = 'not found';
var category, categoryGroup, found = false;
for (i = 0; i < categoryGroups.length ; i++) {
categoryGroup = categoryGroups[i];
for (j = 0; j < categoryGroup.Categories.length; j++) {
category = categoryGroup.Categories[j];
if (category.Id == id) {
var x = category.Id;
found = true;
break;
}
}
if (found) break;
}
alert(x);
The above code checks if id = 100 is found in the array. If found will alert the value else alerts that its not found. value '100' has been hardcoded for the sake of demo
You could wrap it inside a function to get rid of the awkward break; syntax and you can load each element into a variable inside the for(;;) construct to shave off a few lines.
function subCategoryExists(groups, id)
{
for (var i = 0, group; group = groups[i]; ++i) {
for (var k = 0, category; category = group.Categories[k]; ++k) {
if (category.Id == id) {
return true;
}
}
}
return false;
}
var found = subCategoryExists(categoryGroups, 100);
Easy way using lodash library of NodeJS (assuming you are using NodeJS):
const _ = require('lodash');
let category ;
let categoryGroup = _.find(categoryGroups, (element)=>{
category = _.find(element.Categories, {Id : 100});
return category;
});
console.log(categoryGroup); // The category group which has the sub category you are looking for
console.log(category); // The exact category you are looking for
If you want to actually return the inner category (instead of just checking for it's presence) you can use reduce:
return categoryGroups.reduce((prev, curr) => {
//for each group: if we already found the category, we return that. otherwise we try to find it within this group
return prev || curr.Categories.find(category => category.Id === id);
}, undefined);
This short-circuits on the inner categories, and touches each categoryGroup once. It could be modified to short-cicuit on the categoryGroups as well.
Here's a JS Fiddle demonstration.
You could use underscore:
var cat = _(categoryGroups).
chain().
pluck('Categories').
flatten().
findWhere({Id: 2}).
value();
What I'm doing here is that I'm extracting all Categories values in a single array and then grepping for the correct categories.
EDIT: sorry, didn't get your question right the first time. As the comments suggest, you might not want to use underscore just for that, but that's how I would do it :)
We are using object-scan for our data processing now. It's very powerful once you wrap your head around it. For your questions this would look like this:
// const objectScan = require('object-scan');
const lookup = (id, data) => objectScan(['Categories.Id'], {
useArraySelector: false,
abort: true,
rtn: 'parent',
filterFn: ({ value }) => value === id
})(data);
const categoryGroups = [{ Id: 1, Categories: [{ Id: 1 }, { Id: 2 }] }, { Id: 2, Categories: [{ Id: 100 }, { Id: 200 }] }];
console.log(lookup(1, categoryGroups));
// => { Id: 1 }
console.log(lookup(100, categoryGroups));
// => { Id: 100 }
console.log(lookup(999, categoryGroups));
// => undefined
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan#13.8.0"></script>
Disclaimer: I'm the author of object-scan

Categories

Resources