texts in array converts to objects - javascript

I am using node to convert an array to object, I have an array looks like this
[
'items[0].book=Book1',
'items[0].color=Red',
'items[0].bookCode=#1',
'items[1].book=Book2',
'items[1].color=Yellow',
'items[1].bookCode=#2',
'items[2].book=Book3',
'items[2].color=Blue',
'items[2].bookCode=#3',
...
]
I am trying to convert it to be objets in one array
items:[
{
book: "Book1",
color: "Red",
bookCode: "#1"
},
{
book: "Book2",
color: "Yellow",
bookCode: "#2"
},
...
]
I found it is easy to conver it uses a 3rd party lib like setKeypath/set,
const obj = {};
const arr = [items......(like above)]
arr.forEach((val => {
if (val.startsWith('items[')) {
const splitWord = item.split('=');
setKeypath(obj, splitWord[0], splitWord[1]);
}
});
I am seeking a way if it can be done the same output with es6, so I don't really need a library. Thanks

const items = [
"items[0].book=Book1",
"items[0].color=Red",
"items[0].bookCode=#1",
"items[1].book=Book2",
"items[1].color=Yellow",
"items[1].bookCode=#2",
"items[2].book=Book3",
"items[2].color=Blue",
"items[2].bookCode=#3"
];
let res = [];
let currId = "";
let currItem = null;
for (let i = 0; i < items.length; i++) {
let parts = items[i].split(".");
if (currId!==parts[0] && currItem) { //new item
res.push(currItem)
currId = parts[0];
}
if (!currItem)
currItem = {};
let keyValue = parts[1].split("=");
currItem[keyValue[0]] = keyValue[1]
}
console.log({items: res})

You may first find all values by regex, and insert the attribute to each corresponding element one by one. This approach works for whatever ordering the array is, and whatever attributes there are, as long as each element follow the same pattern.
let items = [
"items[1].bookCode=#2",
"items[0].book=Book1",
"items[0].bookCode=#1",
"items[1].book=Book2",
"items[2].bookCode=#3",
"items[1].color=Yellow",
"items[2].book=Book3",
"items[2].color=Blue",
"items[0].color=Red",
"items[4].test=test!"
];
let indexPattern = /\[(\d*)\]/;
let attrPattern = /\.(.*)=/;
let valuePattern = /=(.*)/;
let obj = Object.values(
items.reduce((obj, element) => {
let index = element.match(indexPattern)[1];
let attr = element.match(attrPattern)[1];
let value = element.match(valuePattern)[1];
if (!obj.hasOwnProperty(index)) obj[index] = {};
obj[index][attr] = value;
return obj;
}, {})
);
console.log(obj);

[
'items[0].book=Book1',
'items[0].color=Red',
'items[0].bookCode=#1',
'items[1].book=Book2',
'items[1].color=Yellow',
'items[1].bookCode=#2',
'items[2].book=Book3',
'items[2].color=Blue',
'items[2].bookCode=#3',
].reduce((acc, str) => {
const index = Number(str.slice(str.indexOf('[') + 1, str.indexOf(']')));
if (!acc[index]) {
acc[index] = {};
}
const entry = [str.slice(str.indexOf('.') + 1, str.indexOf('=')), str.slice(str.indexOf('=') + 1)];
acc[index][entry[0]] = entry[1];
return acc;
}, []);
Here I pick apart the string you're given based on the consistent format, grab the index, key, and value, and then just use Array#reduce to do the work of putting the array together.
Documentation:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce

I think a smattering of regex would do the trick:
const ar = [
'items[0].book=Book1',
'items[0].color=Red',
'items[0].bookCode=#1',
'items[1].book=Book2',
'items[1].color=Yellow',
'items[1].bookCode=#2',
'items[2].book=Book3',
'items[2].color=Blue',
'items[2].bookCode=#3'
]
const result = [];
ar.forEach(item => {
const index = parseInt(item.match(/\[([0-9]+)\]/)[1]);
const params = item.split(".")[1].split("=");
if(!result[index])
result[index] = {}
result[index][params[0]] = params[1];
})
console.log(result)
Note that item.match(/\[([0-9]+)\]/) matches the number inside your brackets. match returns an array where 1 is the index of the actual value between the brackets.

Related

replace nested for loop with array helpers - finding priority element

I am looking to replace nested for loop implementation with array helpers for optimization,
The function returns string from testArray, which contains the highest priority as per priorityArray.
const arr1 = ["A_COUNTY","B_STATE","C_COUNTRY", "D_CONTINENT","E_WORLD"];
const arr2 = ["X_COUNTY","Y_STATE","Z_PLANET"];
const priorityArray = ["CONTINENT","COUNTRY","STATE","COUNTY"];
function findPriorityElement(testArray:string[])
{
for (let i = 0; i <priorityArray.length; i++)
for (let j = 0; j <testArray.length; j++) {
if (testArray[j].includes(priorityArray[i]))
return testArray[j];
}
return "";
}
console.log(findPriorityElement(arr1)); // Result: "D_CONTINENT"
console.log(findPriorityElement(arr2)); // Result: "Y_STATE"
Any leads is appreciated.
You could use find() instead of the inner for loop
const arr1 = ["A_COUNTY", "B_STATE", "C_COUNTRY", "D_CONTINENT", "E_WORLD"];
const arr2 = ["X_COUNTY", "Y_STATE", "Z_PLANET"];
const priorityArray = ["CONTINENT", "COUNTRY", "STATE", "COUNTY"];
function findPriorityElement(testArray) {
for (p of priorityArray) {
const match = testArray.find(e => e.includes(p));
if (match) {
return match;
}
}
}
console.log(findPriorityElement(arr1)); // Result: "D_CONTINENT"
console.log(findPriorityElement(arr2)); // Result: "Y_STATE"
const arr1 = ["A_COUNTY", "B_STATE", "C_COUNTRY", "D_CONTINENT", "E_WORLD"];
const arr2 = ["X_COUNTY", "Y_STATE", "Z_PLANET"];
const priorityArray = ["CONTINENT", "COUNTRY", "STATE", "COUNTY"];
const findPriorityElement = testArray => {
for (const priorityElement of priorityArray) {
const elem = testArray.find(testElement => testElement.includes(priorityElement));
if (elem) {
return elem;
}
}
};
console.log(findPriorityElement(arr1)); // Result: "D_CONTINENT"
console.log(findPriorityElement(arr2)); // Result: "Y_STATE"

Convert array of strings into array of objects where each string itself has a separator

I have an array of strings:
["aa-q1-true", "bb-q1-false", "cc-q1-true", "aa-q2-true", "xx-q2-false", "yy-q2-true", "mm-q3-true", "mn-q3-false", "qr-q3-false"]
Where each string has a meaning. For example if we consider the first string i.e., "aa-q1-true"
Here the first part - aa is the quiz answer, middle part - q1 is the question and true, the last part is the answer status. Status can be true or false. This rule applies for each of the string inside the array.
Now I want to convert it into an array of objects like the following -
[
0: [{
quizAns: [{aa: true}, {bb: false}, {cc: true}]
quizQuestion: q1
}]
1: [{
quizAns: [{aa: true}, {xx: false}, {yy: true}]
quizQuestion: q2
}]
2: [{
quizAns: [{mm: true}, {mn: false}, {qr: false}]
quizQuestion: q3
}]
]
I just could not able to apprehend the logic to do this on my own. If you can just gimme some ideas or solution, it would be very helpful. Thank you so much for your time.
You would want to split each item by -, and then you get the first and last item to get your answer object, and the middle to get which question it belongs to, then you just iteratively construct the solution:
let ans = ["aa-q1-true", "bb-q1-false", "cc-q1-true", "aa-q2-true", "xx-q2-false", "yy-q2-true", "mm-q3-true", "mn-q3-false", "qr-q3-false"].map((s) => s.split('-')).reduce((carry, current) => {
let existingIndex = carry.findIndex((item) => item.quizQuestion === current[1]);
if (existingIndex === -1) {
carry.push({quizAns: [], quizQuestion: current[1]});
existingIndex = carry.length - 1;
}
carry[existingIndex].quizAns.push({[current[0]]: current[2]});
return carry;
}, []);
console.log(ans);
Just another approach with less code
const resultMap = array.reduce((acc, item) => {
// current data
const [answer, quizQuestion, status] = item.split("-");
// previous answers
const previousAnswers = acc[quizQuestion] ? acc[quizQuestion].quizAns : [];
// new answers
const newAnswers = [...previousAnswers, { [answer]: status }];
return { ...acc, [quizQuestion]: { quizQuestion, quizAns: newAnswers } };
}, {});
const result = Object.values(resultMap)
console.log(result)
Just an another approach. Has less looping over the array than the other answer.
let questions = { };
let ans = ["aa-q1-true", "bb-q1-false", "cc-q1-true", "aa-q2-true", "xx-q2-false", "yy-q2-true", "mm-q3-true", "mn-q3-false", "qr-q3-false"]
.reduce((acc, curr) => {
let elements = curr.split("-");
let obj = {};
obj[elements[0]] = elements[2];
if (questions[elements[1]]) {
questions[elements[1]].push(obj);
} else {
questions[elements[1]]= [obj];
}
}, {})
let result = [];
for (let prop in questions) {
result.push({ "quizAns": questions[prop], "quizQuestion": prop });
}
console.log(result);
I am not a big fan of array.reduce syntax cause of readability factor.
This loop will work, even if the input comes in a scrambled fashion.
const a = ["aa-q1-true", "bb-q1-false", "cc-q1-true", "aa-q2-true", "xx-q2-false", "yy-q2-true", "mm-q3-true", "mn-q3-false", "qr-q3-false"];let results = [];
const retObj = (que, ans) => {
const obj = {};
obj[`${que}`] = ans;
return obj;
};
for (let i in a){
let currEleArr = a[i].split("-");
let tarInd = parseInt(currEleArr[1].split("")[1]) - 1;
let que = currEleArr[0];
let ans= currEleArr[2];
if (!results[tarInd])
results[tarInd] = [{quizAns: []}];
if(!results[tarInd][0].quizQuestion)
results[tarInd][0]["quizQuestion"] = `q${tarInd + 1}`;
results[tarInd][0].quizAns.push(retObj(que, ans));
}
console.log(results);

How to make the grouping fastest, provided that the data in the array also show the object nodeJs

Hello this is my array;
arr =[{cNo:1,buyOrSel:'A',ip:192.168.1.1},{cNo:1,buyOrSel:'S',ip:192.168.1.1},{cNo:2,buyOrSel:'S',ip:192.168.1.2},{cNo:3,buyOrSel:'A',ip:192.168.1.1},{cNo:4,buyOrSel:'S',ip:192.168.1.3},{cNo:5,buyOrSel:'S',ip:192.168.1.2}]
I want to group in Object like this;
[{cNo:'1,3',ip:192.168.1.1},{cNo:'2,5',ip:192.168.1.2}]
I don't want to use nested For loop.What is the best way for this ?
let arr = [{cNo:1,buyOrSel:'A',ip:"192.168.1.1"},{cNo:1,buyOrSel:'S',ip:"192.168.1.1"},{cNo:2,buyOrSel:'S',ip:"192.168.1.2"},{cNo:3,buyOrSel:'A',ip:"192.168.1.1"},{cNo:4,buyOrSel:'S',ip:"192.168.1.3"},{cNo:5,buyOrSel:'S',ip:"192.168.1.2"}];
arr = arr.reduce((prev, a) => {
let cNo = prev[a.ip] = prev[a.ip] || [];
if (cNo.indexOf(a.cNo) < 0) {
prev[a.ip].push(a.cNo);
};
return prev
}, {});
arr = Object.keys(arr).map(key => {
return {
cNo: arr[key].join(),
ip: key
}
});
console.log(arr);
you can use object for better grouping.
and instead of pushing only cNo, you can push whole object too.
let arr = [{cNo:1,buyOrSel:'A',ip:"192.168.1.1"},{cNo:1,buyOrSel:'S',ip:"192.168.1.1"},{cNo:2,buyOrSel:'S',ip:"192.168.1.2"},{cNo:3,buyOrSel:'A',ip:"192.168.1.1"},{cNo:4,buyOrSel:'S',ip:"192.168.1.3"},{cNo:5,buyOrSel:'S',ip:"192.168.1.2"}];
let objectByIP = {};
arr.forEach(element => {
if (!objectByIP[element.ip]) {
objectByIP[element.ip] = [element.cNo];
} else {
objectByIP[element.ip].push(element.cNo);
}
});
console.log(objectByIP);
console.log(Object.keys(objectByIP));
you can use map():
let a = arr.map(a=>{ return {cNo: a.cNo, ip: a.ip}})

Filtering one value and creating new array in JavaScript

I want to filter one value from my first array and create a second array with the filtered value.
So far I have that but it does not seem very efficient.
const blacklist = bookingsList.filter(booking => booking.id === id);
const newBookingList = bookingsList.filter(booking => booking.id !== id);
Is there a better way to do this?
I think something like this would be good on a large array or if testing the condition is expensive because you would only loop through the array once
const array1 = [];
const array2 = [];
for (var i = 0; i < input.length; i++) {
const value = input[i];
( testCondition(value) ? array1 : array2 ).push(value);
}
You can do it with a single iteration by using forLoop like
const blacklist = [];
const newBookingList = [];
bookingsList.forEach(booking => {
if(booking.id === id) {
blacklist.push(booking)
}
else {
newBookingList.push(booking)
}
}
You can use forEach() and ternary operator:
const bookingsList = [{id:'black'},{id:'new'}];
const blacklist = [], newBookingList = [], id='black';
bookingsList.forEach(booking => booking.id === id? blacklist.push(booking.id) : newBookingList.push(booking.id));
console.log(blacklist);
console.log(newBookingList);
let blacklist = []
let newBookingList = []
const ID = 10;
let bookingsList=[{id:10}, {id:20}]
bookingsList.forEach(booking => booking.id === ID ? blacklist.push(booking) : newBookingList.push(booking))
console.log(newBookingList)
console.log(blacklist)
I think you can use forEach for that:
const newBookingList = [];
const blacklist = [];
bookingsList.forEach(function(booking) {
if(booking.id === id){
blacklist.push(booking)
}
if(booking.id !== id){
newBookingList.push(booking)
}
})
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach
You can use splice method to retrive and remove filtered value.
var bookingList = [{id : 1, name : "A"}, {id: 2, name: "B"}];
var sBookingList = bookingList.splice(bookingList.map(function(b){return b.name}).indexOf("A"), 1);
console.log(sBookingList);
console.log(bookingList);

Combine elements of an array into a sub array if they are the same

Trying to find an algorithm that will do the following:
let input = [ 'kooty', 'dlnnoo', 'emor', 'dlnnoo', 'kooty', 'aiprs' ]
function combine(input){
// you should return
[ ['kooty', 'kooty'], ['dlnnoo','dlnnoo'], ['emor'], ['aiprs'] ]
}
I got the answer by using Lodash but i was wondering if there was a way without
function combine(input){
let sortedCity = [];
let finalArr = [];
for(let city in input){
sortedCity.push(input[city].toLowerCase().split('').sort().join(''));
}
let a = lodash.groupBy(sortedCity)
return Object.values(a)
}
combine(input)
let input = [ 'kooty', 'dlnnoo', 'emor', 'dlnnoo', 'kooty', 'aiprs' ]
function combine (input) {
let sorted = input.map(cur => cur.toLowerCase().split('').sort().join(''))
let cache = {} // cache value to index of result array
return sorted.reduce((sum, cur) => {
let index = cache[cur]
if (index !== undefined) {
sum[index].push(cur)
} else {
sum.push([cur])
cache[cur] = sum.length - 1
}
return sum
}, [])
}
combine(input)

Categories