So I'm reformatting my data and I noticed that my data isn't quite getting restructured the way I want it to. I noticed that my results come back as
[
{
"name": "sites",
"parent": null,
"count": 3
},
{
"name": "group1",
"parent": "sites",
"count": 3
},
{
"name": "bk",
"parent": "group1",
"count": 3
},
{
"name": "sitepages",
"parent": "bk",
"count": 1
},
{
"name": "home.aspx",
"parent": "sitepages",
"count": 1
}
]
It isn't grabbing my "not matches". I've spent so much time looking it over and I'm coming to a blank. It should be
[
{
"name": "sites",
"parent": null,
"count": 3
},
{
"name": "group1",
"parent": "sites",
"count": 3
},
{
"name": "bk",
"parent": "group1",
"count": 3
},
{
"name": "sitepages",
"parent": "bk",
"count": 1
},
{
"name": "home.aspx",
"parent": "sitepages",
"count": 1
},
{
"name": "tester",
"parent": "bk",
"count": 1
},
{
"name": "tester",
"parent": "home.aspx",
"count": 1
},
{
"name": "_layouts",
"parent": "bk",
"count": 1
},
{
"name": "15",
"parent": "_layouts",
"count": 1
},
{
"name": "upload.aspx",
"parent": "15",
"count": 1
},
]
I believe something is missing in my loop.
var testArr = [
{
Author: { Title: "Mitchell" },
BrowserType: "FireFox",
Created: "2017-04-25T16:39:40Z",
pathname: "sites/group1/bk/sitepages/home.aspx"
},
{
Author: { Title: "Pierre" },
BrowserType: "Opera",
Created: "2017-04-25T16:39:40Z",
pathname: "sites/group1/bk/tester/home.aspx"
},
{
Author: { Title: "Mizell" },
BrowserType: "IE",
Created: "2017-04-25T16:47:02Z",
pathname: "sites/group1/bk/_layouts/15/upload.aspx"
}
];
function reduceData(data) {
var root = null;
var newArr = null;
var itemContainer = [];
var masterArr = [];
var filteredArr = [];
data.forEach(function (props, idx) {
//check the last character of the pathname is "/" and removes it
if (props.pathname.charAt(props.pathname.length - 1) === "/") {
props.pathname = props.pathname.substring(0, props.pathname.length - 1);
}
//lowercase the pathname + split into strings
props.pathname = props.pathname.toLowerCase().split("/");
//format the pathname
var lastItem = "";
newArr = props.pathname.reduce(function (acc, props, index) {
if (acc.length === 0) {
acc.push({ name: props, parent: null, count: 1 });
lastItem = props;
} else {
acc.push({ name: props, parent: lastItem, count: 1 });
lastItem = props;
}
return acc;
}, []);
//The first iteration
if (idx === 0) {
itemContainer = newArr;
} else {
for (var i = 0; i < itemContainer.length; i++) {
// Loop for newArr
for (var j = 0; j < newArr.length; j++) {
//compare the element of each and every element from both of the arrays
//console.log(masterArr[i], newArr[j]);
if (
itemContainer[i].name === newArr[j].name &&
itemContainer[i].parent === newArr[j].parent
) {
//Match
masterArr[i] = {
name: itemContainer[i].name,
parent: itemContainer[i].parent,
count: itemContainer[i].count++
};
} else {
//Doesn't Match
masterArr[i] = {
name: itemContainer[i].name,
parent: itemContainer[i].parent,
count: itemContainer[i].count
};
}
}
}
}
});
console.log(masterArr)
}
reduceData(testArr)
ok.. I revamp your code a little..
delete the if else after the //The first iteration, and use this instead..
newArr.forEach((newEl) => {
const matchIdx = masterArr.findIndex((masterEl) => masterEl.name === newEl.name && masterEl.parent === newEl.parent);
if(matchIdx < 0){
masterArr.push(newEl);
}
else {
masterArr[matchIdx].count = masterArr[matchIdx].count + 1;
}
});
Related
This is, I believe a very simple question for a JS programmer. Given the following array of objects named "categories"
[
{
"id": 1,
"name": "FURNITURE",
},
{
"id": 2,
"name": "AUTOMOTIVE",
},
{
"id": 3,
"name": "UPHOLSTERY",
}
]
I want to push the "name" on the "selectedCategories" array below where "id" === "id"
[
{
"id": 1
},
{
"id": 3
}
]
Below is my attempt to solve this, but ... not working..
for (let i = 0; i < selectedCategories.length; i++) {
for(let y = 0; y < categories.length; y++){
selectedCategories.name = categories[y].name
}
}
I believe this is what you are looking for
selectedCategories = selectedCategories.map(el => {
const searchEl = categories.find(e => e.id === el.id);
if (searchEl)
return { ...el, name: searchEl.name }
return el;
});
const categories = [
{
"id": 1,
"name": "FURNITURE",
},
{
"id": 2,
"name": "AUTOMOTIVE",
},
{
"id": 3,
"name": "UPHOLSTERY",
}
];
var selectedCategories = [
{
"id": 1,
},
{
"id": 3,
}
];
selectedCategories = selectedCategories.map(
selectedCategory => categories.find(category => category.id === selectedCategory.id),
);
console.log(selectedCategories);
i have array of object which are sorted based on date/time, I have to form an array of object with latest data from each date?. I'm getting the solution with for loop but I need to use es6 and above, please help me with a better and advanced solution.
var array = [
{
"id": 1,
"date": "2016-01-15T16:18:44.258843Z",
"status": "NEW",
"request": 4
},
{
"id": 2,
"date": "2016-01-15T18:18:44.258843Z",
"status": "NEW",
"request": 4
},
{
"id": 3,
"date": "2016-01-15T20:18:44.258843Z",
"status": "NEW",
"request": 4
},
{
"id": 4,
"date": "2016-01-19T16:18:44.258843Z",
"status": "STD",
"request": 4
},
{
"id": 6,
"date": "2016-01-23T17:18:44.258843Z",
"status": "FOR",
"request": 4
},
{
"id": 5,
"date": "2016-01-23T16:18:44.258843Z",
"status": "FOR",
"request": 4
}]
const list = filter(array, el => (el.date));
latestDate = list[0]?.date.slice(0, 10);
latestResponse.push(res[0]);
for (let i = array.length - 1; i > 0; i--) {
if (this.latestDate !== array[i].date.slice(0, 10)) {
latestDate = (array[i].date).slice(0, 10);
latestResponse.push(res[i]);
}
}
expected Output
var array = [
{
"id": 3,
"date": "2016-01-15T20:18:44.258843Z",
"status": "NEW",
"request": 4
},
{
"id": 4,
"date": "2016-01-19T16:18:44.258843Z",
"status": "STD",
"request": 4
},
{
"id": 5,
"date": "2016-01-23T17:18:44.258843Z",
"status": "FOR",
"request": 4
}]
Here is the code, use reduce method.
var array = [
{
"id": 1,
"date": "2016-01-15T16:18:44.258843Z",
"status": "NEW",
"request": 4
},
{
"id": 2,
"date": "2016-01-15T18:18:44.258843Z",
"status": "NEW",
"request": 4
},
{
"id": 3,
"date": "2016-01-15T20:18:44.258843Z",
"status": "NEW",
"request": 4
},
{
"id": 4,
"date": "2016-01-19T16:18:44.258843Z",
"status": "STD",
"request": 4
},
{
"id": 6,
"date": "2016-01-23T17:18:44.258843Z",
"status": "FOR",
"request": 4
},
{
"id": 5,
"date": "2016-01-23T16:18:44.258843Z",
"status": "FOR",
"request": 4
}];
console.clear();
let results = array.reduce((acc: any, cur: any) => {
let curDate = new Date(cur.date);
let curDateStr = curDate.getFullYear() + '-' + (curDate.getMonth() + 1) + '-' + curDate.getDate();
let existDateObj = acc.find(f => f.dStr === curDateStr);
if (existDateObj) {
let existDate = new Date(existDateObj.date);
if (curDate.getTime() > existDate.getTime()) {
acc = acc.filter(f => f.id !== existDateObj.id);
acc.push(Object.assign(cur, { dStr: curDateStr }));
}
} else {
acc.push(Object.assign(cur, { dStr: curDateStr }));
}
return acc;
}, []);
console.log(results);
You can try this. Sort, group, get
array.sort(function(a,b){
return new Date(a.date) - new Date(b.date);
});
var newArray=[];
result = array.reduce(function (r, a) {
r[a.status] = r[a.status] || [];
r[a.status].push(a);
return r;
}, Object.create(null));
for (const n in result) {
newArray.push(result[n].slice(-1))
}
Instead of using multiple array methods/loops, this can be done using a single for of loop as follows,
function getLatestDateInfo(array) {
let obj = {};
for (const item of array) {
var today = new Date(item.date);
var year = today.getFullYear();
var month = today.getMonth() + 1;
var day = today.getDate();
var mainDate = day + "-" + month + "-" + year;
obj = {
...obj,
[mainDate]: { ...item },
}
}
return Object.values(obj);
}
The above solution is based on the assumption that the array of objects are sorted based on date/time, as this is mentioned in the question.
const latestResponse = [];
const list = filter(res, el => (el.date));
let latestDate = moment(list[0]?.date).format("DD/MM/YYYY");
[...res].reverse().filter(el => {
if (latestDate !== moment(el.date).format("DD/MM/YYYY")) {
latestDate = moment(el.date).format("DD/MM/YYYY");
latestResponse.push(el);
}
})
used moment to format the date, and used filter to generate the new array
var array = [
{
"id": 1,
"date": "2016-01-15T16:18:44.258843Z",
"status": "NEW",
"request": 4
},
{
"id": 2,
"date": "2016-01-15T18:18:44.258843Z",
"status": "NEW",
"request": 4
},
{
"id": 3,
"date": "2016-01-15T20:18:44.258843Z",
"status": "NEW",
"request": 4
},
{
"id": 4,
"date": "2016-01-19T16:18:44.258843Z",
"status": "STD",
"request": 4
},
{
"id": 6,
"date": "2016-01-23T17:18:44.258843Z",
"status": "FOR",
"request": 4
},
{
"id": 5,
"date": "2016-01-23T16:18:44.258843Z",
"status": "FOR",
"request": 4
}];
const sortedLatestArray = array.sort((a, b) => {
if(a.date > b.date) {
return -1;
} else if (a.date === b.date){
return 0;
} else {
return 1;
}
}).filter((item, i, sortedArray) => {
if(i) {
const prevItem = sortedArray[i - 1];
const prevItemDate = prevItem.date.slice(0, 10);
const currentItemDate = item.date.slice(0, 10);
return currentItemDate !== prevItemDate;
}
return true;
}).reverse();
console.log(sortedLatestArray);
Sort array latest to oldest, and then remove duplicated date item by comparing former Item and current Item using filter method, finally reverse array to make result sorted as oldest to latest.
Use forEach and build an object with keys as date. If the same date exist, then replace with latest date-time.
const refine = (arr) => {
const res = {};
arr.forEach((item) => {
const date_str = item.date.split('T')[0];
res[date_str] ??= { ...item };
if (new Date(item.date) > new Date(res[date_str].date)) {
res[date_str].date = item.date;
}
});
return Object.values(res);
};
var array = [
{
id: 1,
date: "2016-01-15T16:18:44.258843Z",
status: "NEW",
request: 4,
},
{
id: 2,
date: "2016-01-15T18:18:44.258843Z",
status: "NEW",
request: 4,
},
{
id: 3,
date: "2016-01-15T20:18:44.258843Z",
status: "NEW",
request: 4,
},
{
id: 4,
date: "2016-01-19T16:18:44.258843Z",
status: "STD",
request: 4,
},
{
id: 6,
date: "2016-01-23T17:18:44.258843Z",
status: "FOR",
request: 4,
},
{
id: 5,
date: "2016-01-23T16:18:44.258843Z",
status: "FOR",
request: 4,
},
];
console.log(refine(array));
I have an array of objects with the following format
var arr = [
{
"productId": "123456",
"productName": "Test Product 1",
"description": [
"This is delicious",
"Suitable for vegetarian"
],
"attributes": {
"internalId": "091283"
"category": "Dairy"
},
"order": 1
}
];
And I am trying to map into something like below
[
[{
{
"name": "productId",
"value": "123456"
},
{
"name": "productName",
"value": "Test Product 1"
},
{
"name": "description",
"value": ["This is delicious", "Suitable for vegetarian"]
},
{
"name": "attributes",
"value": {
{
"name": "internalId",
"value": "091283"
},
{
"name": "category",
"value": "Dairy"
}
}
},
{
"name": "order",
"value": 1
}
}]
]
I tried mapping simple properties before going further and now stuck at getting only the last property of each object in the loop.
Suppose I don't know what are the format of incoming data and how can I normalize the JSON object to the format I want?
normalizeJson = (array) => {
for(i = 0; i < array.length; i++){
normalizedJson[i] = {};
Object.keys(array[i]).forEach(key => {
if (array[i][key] && typeof array[i][key] === "object") {
// normalizeJson(obj[key]);
// console.log(key + ' is object');
return;
} else {
o = {};
o["name"] = key;
o["value"] = array[i][key];
normalizedJson[i] = o;
// normalizedJson[i]["name"] = key;
// normalizedJson[i].value = array[i][key];
// console.log(key);
return;
}
});
}
console.log(normalizedJson);
};
Or is there any library I can use in order to achieve this?
Try this
var obj = [
{
productId: "123456",
productName: "Test Product 1",
description: ["This is delicious", "Suitable for vegetarian"],
attributes: {
internalId: "091283",
category: "Dairy",
},
order: 1,
},
];
function normalizeObject(obj) {
var result = [];
if (Array.isArray(obj)) {
for (let i of obj) {
result.push(normalizeObject(i));
}
} else if (typeof obj == "object") {
for (let i of Object.keys(obj)) {
result.push({ name: i, value: normalizeObject(obj[i]) });
}
} else {
return obj;
}
return result;
}
console.log(JSON.stringify(normalizeObject(obj), null, 2));
This looping method called recursion. Which is loop by calling function itself.
I have 3 arrays of 3 different types. Each array contains the count of an id (which might be duplicate like arrayOfB).
Each id has a limit value of count property is 10 (the count includes different types. Ex: if unique1 has 10 counts in type A, when process type B for unique1, it will be not processed).
const arrayOfA = [
{
"type": "A", "count": 10, "id": "UID1"
},
{
"type": "A", "count": 20, "id": "UID2"
},
{
"type": "A", "count": 1, "id": "UID4"
},
];
const arrayOfB = [
{
"type": "B", "count": 5, "id": "UID1"
},
{
"type": "B", "count": 5, "id": "UID3"
},
];
const arrayOfC = [
{
"type": "C", "count": 6, "id": "UID1"
},
{
"type": "C", "count": 6, "id": "UID4"
},
{
"type": "C", "count": 3, "id": "UID2"
},
{
"type": "C", "count": 3, "id": "UID3"
},
]
The output will be like:
Map {
'UID1|A' => 10,
'UID2|A' => 10,
'UID4|A' => 1,
'UID3|B' => 5,
'UID4|C' => 6 }
I used a set to hold id, which already has the maximum count and map to hold the output.
const maxed = new Set();
const elements = new Map();
arrayOfA.forEach(element => {
if (element.count > 10) {
maxed.add(`${element.id}`);
elements.set(`${element.id}|${element.type}`, 10);
console.log(elements)
return;
}
if (elements.has(`${element.id}|${element.type}`)) {
const newCount = elements.get(`${element.id}|${element.type}`) + element.count;
newCount > 10 ? elements.set(`${element.id}|${element.type}`, 10) : elements.set(`${element.id}|${element.type}`, newCount);
console.log(elements)
return;
}
elements.set(`${element.id}|${element.type}`, element.count);
});
arrayOfB.forEach(element => {
if (maxed.has(`${element.id}`)) {
console.log(elements)
return;
}
const countOfA = elements.has(`${element.id}|A`) ? elements.get(`${element.id}|A`) : 0;
let newCount = countOfA + element.count;
if (elements.has(`${element.id}|${element.type}`)) {
newCount = newCount + element.get(`${element.id}|${element.type}`);
}
if (newCount > 10) {
maxed.add(`${element.id}`);
if ((10 - countOfA) > 0) elements.set(`${element.id}|${element.type}`, 10 - countOfA);
console.log(elements)
return;
}
elements.set(`${element.id}|${element.type}`, element.count);
})
arrayOfC.forEach(element => {
if (maxed.has(`${element.id}`)) {
console.log(elements)
return;
}
const countOfA = elements.has(`${element.id}|A`) ? elements.get(`${element.id}|A`) : 0
const countOfB = elements.has(`${element.id}|C`) ? elements.get(`${element.id}|C`) : 0
let newCount = countOfA + countOfB + element.count;
if (elements.has(`${element.id}|${element.type}`)) {
newCount = newCount + element.get(`${element.id}|${element.type}`);
}
if (newCount > 10) {
maxed.add(`${element.id}`);
if ((10 - countOfA - countOfB) > 0); elements.set(`${element.id}|${element.type}`, 10 - countOfA - countOfB);
console.log(elements)
return;
}
elements.set(`${element.id}|${element.type}`, element.count);
})
I want to ask about another faster implementation if any. I estimated my big O will be O(n) (n is the total length of 3 arrays). If elements of arrays do not contain the same id.
Edit:
Big thanks to you all, but seems like there's one edge case. The answers couldn't handle
var arrayOfA = [
{
"type": "A", "count": 10, "id": "UID1"
},
{
"type": "A", "count": 20, "id": "UID2"
},
{
"type": "A", "count": 1, "id": "UID4"
},
];
const arrayOfB = [
{
"type": "B", "count": 5, "id": "UID1"
},
{
"type": "B", "count": 5, "id": "UID3"
},
{
"type": "B", "count": 1, "id": "UID3"
},
];
var arrayOfC = [
{
"type": "C", "count": 6, "id": "UID1"
},
{
"type": "C", "count": 6, "id": "UID4"
},
{
"type": "C", "count": 3, "id": "UID2"
},
{
"type": "C", "count": 3, "id": "UID3"
},
]
In arrayOfB, I have the UID3 occurs twice, so your answers doesn't seem to work on that case.
Instead of a Set for a maxed id, you could sum the count for every id and use it for all following arrays.
const
getKey = (...a) => a.join('|'),
rawData = [{ type: "A", count: 10, id: "UID1" }, { type: "A", count: 20, id: "UID2" }, { type: "A", count: 1, id: "UID4" }],
rawData3 = [{ type: "B", count: 5, id: "UID1" }, { type: "B", count: 5, id: "UID3" }],
rawData2 = [{ type: "C", count: 6, id: "UID1" }, { type: "C", count: 6, id: "UID4" }, { type: "C", count: 3, id: "UID2" }, { type: "C", count: 3, id: "UID3" }],
elements = new Map,
sums = new Map;
[rawData, rawData3, rawData2].forEach(a => a.forEach(({ type, count, id }) => {
var sum = sums.get(id) || 0,
key = getKey(id, type);
sums.set(id, sum + count);
if (sum >= 10) return;
if (sum + count > 10) {
if (10 - sum > 0) elements.set(key, 10 - sum);
return;
}
elements.set(key, count);
}));
[...elements].map(a => console.log(a.join(': ')));
.as-console-wrapper { max-height: 100% !important; top: 0; }
Based on the assumption that you have missed to include "B" in your expected results set, two nested loops can provide the manipulation and filtering you want.
function getIdSummary(arrays) {
const maxValue = 10;
//Array of objects which we later conver to a map
//The aim is ease of indexing during the iterations
var summary = []
//A heler to find if a maxed uid is found in the summary
function isMaxed(uid) {
return summary.some(item => {
return item.uid === uid && item.count >= maxValue;
})
}
//Iterate all the arrays
arrays.forEach(anInputArray => {
//Iterate each array
anInputArray.forEach(item => {
if (!isMaxed(item.id)) {
summary.push({uid: item.id, type: item.type, count: item.count > maxValue ? 10 : item.count})
}
})
})
return new Map(summary.map(obj => {
return [obj.uid + '|' + obj.type, obj.count]
}))
}
var arrayOfA = [
{
"type": "A", "count": 10, "id": "UID1"
},
{
"type": "A", "count": 20, "id": "UID2"
},
{
"type": "A", "count": 1, "id": "UID4"
},
];
const arrayOfB = [
{
"type": "B", "count": 5, "id": "UID1"
},
{
"type": "B", "count": 5, "id": "UID3"
},
];
var arrayOfC = [
{
"type": "C", "count": 6, "id": "UID1"
},
{
"type": "C", "count": 6, "id": "UID4"
},
{
"type": "C", "count": 3, "id": "UID2"
},
{
"type": "C", "count": 3, "id": "UID3"
},
]
var m = getIdSummary([arrayOfA, arrayOfB, arrayOfC]);
console.log(Array.from(m));
I have a dummy data like this
i need to create a tree like this
CEO
A
A1
A2
My dummy look like this
let d = [
{
"name": "CEO",
"parent": null,
"id": "5847e80312896b2fa49ce428"
},
{
"name": "A",
"parent": "5847e80312896b2fa49ce428",
"id": "5847f58289046550aa05a49d"
},
{
"name": "A1",
"parent": "5847f58289046550aa05a49d",
"id": "584804b073697edd3d372529"
},
{
"name": "A2",
"parent": "5847e80312896b2fa49ce428",
"id": "584804b073697edd3d372539"
}
];
I have tried somthing like this
var newObj = {tree:d.filter(d=>!d.parent)};
var tree = newObj.tree;
for(let k in tree){
for(let i = 0 ; i < tree.length ; i++){
newObj.tree[i]['child']=this.returnChildNode(tree[i].id,d);
}
}
console.log(newObj);
private returnChildNode(parentId ,arr){
if(arr.filter(d=>d.parent===parentId).length < 1) return null ;
else return arr.filter(d=>d.parent===parentId);
}
Output getting is
{
"tree": [
{
"name": "CEO",
"parent": null,
"id": "5847e80312896b2fa49ce428",
"child": [
{
"name": "A",
"parent": "5847e80312896b2fa49ce428",
"id": "5847f58289046550aa05a49d"
},
{
"name": "A2",
"parent": "5847e80312896b2fa49ce428",
"id": "584804b073697edd3d372539"
}
]
}
]
}
How to complete it by recursion for deep going in generic way
Here's a recursive option using reduce
let d = [{
"name": "CEO",
"parent": null,
"id": "5847e80312896b2fa49ce428"
}, {
"name": "A",
"parent": "5847e80312896b2fa49ce428",
"id": "5847f58289046550aa05a49d"
}, {
"name": "A1",
"parent": "5847f58289046550aa05a49d",
"id": "584804b073697edd3d372529"
}, {
"name": "A2",
"parent": "5847e80312896b2fa49ce428",
"id": "584804b073697edd3d372539"
}]
const rec = (parent, arr) => {
const ref = parent ? parent.id : null
const children = arr.reduce((ac, x) => {
if (x.parent === ref)
ac.push(rec(x, arr))
return ac
}, [])
if (parent)
parent.children = children
return (
parent ?
parent :
children[0]
)
}
const res = rec(null, d)
console.log(res)
You can try this :
var data = [
{
name: "CEO",
parent: null,
id: "0"
},
{
name: "A",
parent: "0",
id: "1"
},
{
name: "A1",
parent: "1",
id: "2"
},
{
name: "A2",
parent: "1",
id: "3"
},
{
name: "A3",
parent: "1",
id: "4"
},
{
name: "b",
parent: "0",
id: "5"
},
{
name: "A1",
parent: "5",
id: "6"
},
{
name: "A2",
parent: "5",
id: "7"
},
{
name: "A3",
parent: "5",
id: "8"
},
];
// data is an array in the above format
function toTree(data) {
var childrenById = {}; // of the form id: [child-ids]
var nodes = {}; // of the form id: {name: children: }
var i, row;
// first pass: build child arrays and initial node array
for (i=0; i<data.length; i++) {
row = data[i];
nodes[row.id] = {name: row.name, children: []};
if (row.parent == null) {
// assume -1 is used to mark the root's "parent"
root = row.id;
} else if (childrenById[row.parent] === undefined) {
childrenById[row.parent] = [row.id];
} else {
childrenById[row.parent].push(row.id);
}
}
// second pass: build tree, using the awesome power of recursion!
function expand(id) {
if (childrenById[id] !== undefined) {
for (var i=0; i < childrenById[id].length; i ++) {
var childId = childrenById[id][i];
nodes[id].children.push(expand(childId));
}
}
return nodes[id];
}
return expand(root);
}
console.log(JSON.stringify(toTree(data)));