I have a Typescript project where I want to join all the values of an Object except one.
This is my Object:
let dataInit = {
"host": "CAPR",
"ua": "RMA",
"country": "VE",
"page":3
};
This is what I do:
let dataJoin = Object.values(dataInit).join(',')
This is what I get:
CAPR,RMA,VE,3
I need to know how to remove the 'page' property, this is what I want:
CAPR,RMA,VE
If you don't know the other attributes, you can first use Object.entries() to give you an array of arrays containing the keys and values. That array can then be filtered to remove the "page" element, mapped to just contain the value, and finally joined.
let dataInit = {
"host": "CAPR",
"ua": "RMA",
"country": "VE",
"page":3
};
console.log(
Object.entries(dataInit)
.filter(([key,val]) => key !== "page")
.map(([_,val]) => val)
.join(",")
)
I would destructure the object first and create a new one
const { host, ua, country } = dataInit
const dataNew = { host, ua, country }
And then call the values join method on the new object.
You could filter the array resulted:
let dataJoin = Object.values(dataInit).filter(element => typeof element === "string").join(",");
Or you can use destructuring as presented in the other comments.
You can use for...in loop to iterate over element and skip page property.
let dataInit = {
"host": "CAPR",
"ua": "RMA",
"country": "VE",
"page": 3
};
const convertObject = (obj) => {
let list = [];
for (const prop in obj) {
if (prop !== "page") {
list.push(obj[prop]);
}
}
return list.join(",");
}
console.log(convertObject(dataInit));
Related
I need convert this object:
{
"en": "[\"En1\",\"En2\"]",
"de": "[\"De1\",\"De2\"]"
}
to:
[
{
"en": "En1",
"de": "De1"
},
{
"en": "En2",
"de": "De2"
}
]
Can you help me?
i tried as follows:
const obj = {
en: '["En1","En2"]',
de: '["De1","De2"]',
};
const result = Object.entries(obj).map(([key, value]) => ({
[key]: JSON.parse(value),
}));
console.log(result)
but this only returns me an array of objects and I don't know how to go ahead and create a new array with key-value matches.
const data = {
"en": "[\"En1\",\"En2\"]",
"de": "[\"De1\",\"De2\"]"
}
console.log(Object.values(Object.entries(data).reduce((a,[k,v])=>
(JSON.parse(v).forEach((e,i)=>(a[i]??={})[k]=e),a),{})))
You can separate your object into 2 arrays then combine them like this
const obj = {
en: '["En1","En2"]',
de: '["De1","De2"]',
};
// Convert to array, separate them by key
let en = JSON.parse(obj.en.split(','));
let de = JSON.parse(obj.de.split(','));
//then combine both arrays
const result = en.map((x,i) =>({
en:x,
de:de[i]
}))
console.log(result);
You can use a combination of Array#map and Array#reduce as follows. The number of items is not hard-corded, so this will work for any number of items:
const
input = { "en": "[\"En1\",\"En2\"]", "de": "[\"De1\",\"De2\"]"},
output = Object.entries(input)
.map(([key,vals]) => JSON.parse(vals).map(v => ({[key]:v})))
//..producing [[{"en":"En1"},{"en":"En2"}],.....]
.reduce(
(obj, values) =>
obj.length === 0 ?
values.map(v => v) : //initial result: [{"en":"En1"},{"en":"En2"}]
values.map((v,i) => ({...obj[i],...v})), //appends "de" prop to each element, ....
[]
);
console.log( output );
I've opted for a more imperative approach here. It appends objects to the returnArray array for each index of the parsed values array. If value matches idx + 1 (eg 'De1'.includes(1)), a key-value pair is initialized on the object at the current index of the returnArray array.
My answer relies on a lot of assumptions regarding the input. If the parsed string array elements are not in numerical order, or there are gaps in the values, eg De3, De1, De2, or De1, De6, the current solution doesn't account for it.
let returnArray = [];
Object.entries(obj).forEach(pair => {
let key = pair[0];
let values = JSON.parse(pair[1]);
values.forEach((_el, idx) => {
if (!returnArray[idx]) {
returnArray.push({});
}
let value = values[idx];
if (value.includes(idx + 1)) {
returnArray[idx][key] = values[idx];
}
});
});
frontendtasks = [
{"id": 1, "name": "User Deletion", "script": "UserDeletion"},
{"id": 2, "name": "User Creation", "script_name": "UserCreation"}
]
backendtasks = [
{"id": 1, "name": "User Deletion", "script": "UserDeletion_V2"}
]
I'm trying to delete the entry with id = 1 in frontendtask and push the entry from backendtask with this code.
if (backendtasks != 0) {
for (updated_task in backendtasks ) {
for (oldtask in frontendtasks) {
if (frontendtasks[oldtask].id == backendtasks[updated_task].id) {
frontendtasks[oldtask] = backendtasks[updated_task]
delete backendtasks[updated_task];
break;
}
}
}
for (new_task in backendtasks) {
frontendtasks.unshift(backendtasks[new_task])
}
}
This is really slow and CPU hits 100% in browser with 700 items. Is there any efficient way to implement this?
Don't loop through both arrays, instead use an object to map backend ids to values:
const mappings = {};
for (const task of backendtasks) {
mappings[task.id] = task;
}
for (let i = 0; i < frontendtasks.length; i ++) {
const curid = frontendtasks[i].id;
if (curid in mappings) {
frontendtasks[i] = mappings[curid];
delete mappings[curid];
}
}
// push is faster than unshift
for (const key in mappings) {
frontendtasks.push(mappings[key]);
}
Approach: Since you have 2 arrays, I would suggest first normalizing backend array to an object, and then iterate on frontend array and lookup in normalized object as lookup in object is O(1) as compared to O(n) in array.
function getFrontendTasks(){
const frontendtasks = [
{"id": 1, "name": "User Deletion", "script": "UserDeletion"},
{"id": 2, "name": "User Creation", "script_name": "UserCreation"}
]
const backendtasks = [
{"id": 1, "name": "User Deletion", "script": "UserDeletion_V2"}
]
const normalizedBackendTasks = backendtasks.reduce((acc, val) => ({...acc, [val.id]: val}), {});
const newFrontendTasks = frontendtasks.map((task) => normalizedBackendTasks[task.id] || task);
return newFrontendTasks
}
console.log(getFrontendTasks())
Creating a mapping table reduces the time complexity from O(n^2) to O(n), by removing the nested for loops, which is very expensive.
Try the following code:
const map = {};
backendtasks.forEach(bt => (map[bt.id] = bt));
frontendtasks.forEach((ft, idx) => {
if (map[ft.id]) {
frontendtasks[idx] = map[ft.id];
delete map[ft.id];
}
});
frontendtasks = frontendtasks.concat(Object.values(map));
Somehow I didn't see the map() function in any solution that creates a new array as shown below.
This will return the new array with the new value. As you can see, it takes an array, an id (this could be anything and any type tho), and a callback.
Searcing for the id in the array and runs the callback when found. Efficient way for what you want to do.
In the callback, I used find() on the backendtasks simply because I need to find the item which has the same id (id: 1).
When found, it returns the item from backendtasks then completes the function by returning that value in the map() function.
So, this should be O(n), considering that the callback only runs once and it's a more elegant solution for multiple uses in my opinion.
const frontendtasks: any[] = [];
const backendtasks: any[] = [];
const fn = (arr: any[], id: number, callback: (removed: any) => any) => {
return arr.map((ft) => {
if (ft.id !== id) return ft;
else return callback(ft);
});
};
fn(frontendtasks, 1, (rm) => backendtasks.find((id) => rm.id === id));
I have an API that response JSON data like this-
{
"unitcode":"apple",
"description":"bus",
"color":"red",
"intent":"Name 1"
}
I want to change like this-
{
"Value1":"apple",
"Value2":"bus",
"value3":"red",
"value4":"sale"
}
Presently, I can code to rename single key but i want some code to replace all key in one shot. my code like this-
request(baseurl)
.then( body => {
var unit = JSON.parse(body);
unit.intent = "sales"
unit.value1 = unit.unitcode
delete unit.unitcode;
console.log(unit)
console.log(unit.Value1)
var unit2 = JSON.stringify(unit)
// let code = unit.description;
conv.ask('Sales is 1 million metric tonnes ' + unit2);
})
please help me out on this and please little elaborate also to learn something new. thanks
Create a Map of original key to new key (transformMap). Convert the object to pairs of [key, value] with Object.entries(), iterate with Array.map() and replace the replacement key from the Map (or the original if not found). Convert back to an object with Object.toEntries():
const transformMap = new Map([
['unitcode', 'Value1'],
['description', 'Value2'],
['color', 'Value3'],
['intent', 'Value4']
]);
const transformKeys = obj =>
Object.fromEntries(
Object.entries(obj)
.map(([k, v]) => [transformMap.get(k) || k, v])
);
const obj = {
"unitcode": "apple",
"description": "bus",
"color": "red",
"intent": "Name 1"
};
const result = transformKeys(obj)
console.log(result)
If you know the object structure and it is constant, you could just use destructing like so.
const data = {
"unitcode":"apple",
"description":"bus",
"color":"red",
"intent":"Name 1"
};
// If the object is fixed and the fields are known.
const mapData = ({ unitcode, description, color, intent }) => ({
Value1: unitcode,
Value2: description,
Value3: color,
Value4: intent
});
console.log(JSON.stringify(mapData(data)));
But if the object has an unknown number of properties:
const data = {
"unitcode":"apple",
"description":"bus",
"color":"red",
"intent":"Name 1"
};
// If the object is fixed and the fields are known.
const mapData = (data) => {
return Object.keys(data).reduce((a,v,i) => {
a[`Value${i+1}`] = data[v];
return a;
}, {});
};
console.log(JSON.stringify(mapData(data)));
You can edit the array to have the values you need
let i=0,j=0,unit1={};
let unit = JSON.parse(body);
let unit3=["val1","val2","val3","val4"]
let unit5=Object.values(unit);
for(let key in unit){
unit1[unit3[i++]]=unit5[j++];
}
var unit2 = JSON.stringify(unit1)
console.log('Sales is 1 million metric tonnes \n' + unit2);
//Sales is 1 million metric tonnes
//{"val1":"apple","val2":"bus","val3":"red","val4":"Name 1"}
Well your target is to modify the keys and retain the value
In that context, you can iterate through your data. To dynamically generate keys as Value1, Value2, etc, we will append Value with iteration index which is going to be unique always.
const modifyInput = (input) => {
const modifiedInput = {}
Object.values(input).forEach((item, index) => {
modifiedInput[`Value${index + 1}`] = item
})
return modifiedInput
}
Use this function, pass your input and get your desired result
I am trying to optimize selecting data from a large table (an array of objects).
I'd like to save multiple values from a single row and then write to localStorage.
let customerTable = [
{
"customer": "Apple Computers",
"contact": "Steve Jobs",
"id": 1,
"city": "Cupertino"
},
{
"customer": "Microsoft",
"contact": "Bill Gates",
"id": 2,
"city": "Redmond"
},
{
"customer": "Microsoft",
"contact": "Satya Nadella",
"id": 3,
"city": "Redmond"
}
]
let selectedRow = customerTable
.filter(i => { return i.customer === selectedCustomer })
.filter(i => { return i.contact === selectedContact })
let id = selectedRow
.map(a => a.id)
.filter((item, pos, self) => {return self.indexOf(item) === pos}) // Remove duplicates
let city = selectedRow
.map(a => a.city)
.filter((item, pos, self) => { return self.indexOf(item) === pos })
Is there a more performant method to selecting multiple values from a data model of this type?
The filter looks fine; however you could filter with composing functions.
Getting unique values can be optimized using Set and reduce:
let id = [...selectedRow.reduce(
(result,item)=>result.add(item.id)
,new Set()
)]
Or as Jonas pointed out (so you don't need reduce):
let id = [...new Set(
selectedRow.map(item=>item.id)
)]
let customerTable = [
{
"customer": "Apple Computers",
"contact": "Steve Jobs",
"id": 1,
"city": "Cupertino"
},
{
"customer": "Microsoft",
"contact": "Bill Gates",
"id": 2,
"city": "Redmond"
},
{
"customer": "Microsoft",
"contact": "Bill Gates",
"id": 2,
"city": "Redmond"
}
]
let selectedCustomer = "Microsoft";
let selectedContact = "Bill Gates";
// [[id], [city]]
let results = customerTable.reduce((_i, _j) => {
if(!(_j.customer === selectedCustomer && _j.contact === selectedContact)) return _i;
_i[0].push(_j.id);
_i[1].push(_j.city);
return _i;
}, [[], []])
.map((v) => v.filter((i, j, a) => a.indexOf(i) === j));
last .map, .filter is for removing duplicated items (as of question) if you are sure there will not be any duplicates you can remove this line,
if there no need for other filtering and no duplicates, the code will look like
let results = customerTable.reduce((_i, _j) => {
if(!(_j.customer === selectedCustomer && _j.contact === selectedContact)) return _i;
_i[0] = _j.id;
_i[1] = _j.city;
return _i;
}, [-1, ""])
In general you want to reduce the number of loops you have, so you shouldn't use multiple array operation when the same result can be achieved with one loop. The operations you're performing can be optimized.
let selectedRow = customerTable
.filter(i => { return i.customer === selectedCustomer })
.filter(i => { return i.contact === selectedContact });
loops through the array twice. It can be rewritten to loop through the array only one time as
let selectedRow = customerTable
.filter(i => {return i.customer === selectedCustomer && i.contact === selectedContact});
The other example also utilize multiple array operations that can be performed in one loop.
Your current code will compute selectedRow as all matching customers and contact pairs in an array and city and id are also arrays of the the unique cities and ids. This can be performed in a single loop as.
// using Set for performance as suggested by #HMR
let selectedRows = [], cities = new Set(), ids = new Set();
for (let i = 0; i = customerTable.length; i++) {
if (customerTable[i].customer === selectedCustomer && customerTable[i].contact === selectedContact) {
selectedRows.push(customerTable[i]);
// include uniqueness contraint on cities and ids
cities.add(customerTable[i].city);
ids.add(customerTable[i].id);
}
}
Depending on where you're getting your data if you can refactor it, you would get better performance on this search with a hashmap (object) with customer, contact, or some combination of both as the keys.
First, why do you have duplicate IDs? Ain't the point of IDs that they are unique?
Aside from optimizing the actual code, you can also optimize the data you are filtering; as mentioned in the comment, it's faster to search a short list than a long one.
So if your Array changes relatively rare compared to the searches you do on the data, it may be worth to create one or more indices. This could be as simple as:
let ixCustomer = new Map();
const ixCustomerKey = item => item.customer.charAt(0).toLowerCase();
const add = (index, key, value) => {
if(index.has(key)) index.get(key).push(value)
else index.set(key, [value]));
}
for(let item of customerTable){
add(ixCustomer, ixCustomerKey(item), item);
}
so that if you search you don't have to search customerTable but only a subset, which if you choose the right way to index the data, should be way smaller than the original Array.
let id = new Set();
let city = new Set();
let arr = ixCustomer.get(ixCustomerKey(selectedCustomer)) || [];
for(let item of arr){
if(item.customer !== selectedCustomer || item.company !== selectedCompany) continue;
id.add(item.id);
city.add(item.city);
}
But you need to know wether this overhead is it worth, for your data and your use-case.
Creating an array based off selected DataTables Rows
$('#savenlp').click(recordjourney);
function recordjourney() {
var data = table.rows(['.selected']).data().toArray();
console.log( (data) );
console.log( JSON.stringify(data) );
}
data returns
0 : (8) ["Which", "TitleCase", "QuestionWord", "", "", "", "", ""]
JSON.stringify(data) returns
[["baseball","Noun","Singular","","","","",""]]
This information is dynamically generated, so I am just looking to take the first value (in this case baseball) and turn it into something like
"baseball": [
"Noun",
"Singular"
]
I can return the first value (the key I want using)
alert(data[0][0]);
I am much more adept in PHP but I am learning javascript/jquery more and more.
It is my understanding javascript does not have associative arrays, so I am a bit confused as to how to generate this.
const data = [
["baseball","Noun","Singular","","","","",""],
["baseballs","Noun","","Plural","","","","",]
];
const mappedData = data.reduce((acc, row) => { acc[row.shift()] = row.filter(d => d !== ''); return acc; }, {});
console.log(mappedData);
We can use object destructuring and spread operators for ease of use.
In the example below, the key will be the first item and all the rest items will be placed in the newData variable
const data = [["baseball","Noun","Singular","","","","",""]];
const [key, ...newData] = data[0]
// if you want the new data to not have empty entries, simple apply the filter
const newDataFiltered = newData.filter(item => !!item)
const objWithEmpty = {[key]: newData}
const objWithoutEmpty = {[key]: newDataFiltered}
console.log(objWithEmpty, objWithoutEmpty)
For multiple arrays inside the outer array, just enclose the whole logic inside a for loop
const data = [
["baseball","Noun","Singular","","","","",""],
["baseball1","Noun1","Singular1","","","","",""],
["baseball2","Noun2","Singular2","","","","",""]
];
const objWithEmpty = {}
const objWithoutEmpty = {}
data.forEach((array) => {
const [key, ...newData] = array
// if you want the new data to not have empty entries, simple apply the filter
const newDataFiltered = newData.filter(item => !!item)
objWithEmpty[key] = newData
objWithoutEmpty[key] = newDataFiltered
})
console.log(objWithEmpty, objWithoutEmpty)
Simply extract the desired values from data and put them into an object formatted as you like:
const data = [["baseball","Noun","Singular","","","","",""]];
const firstArr = data[0];
const transformedFirstObject = {
[firstArr[0]]: [firstArr[1], firstArr[2]],
};
console.log(transformedFirstObject);
But it's pretty weird to have an object with only one property like that. If your data might have more than one sub-array in it and you want to turn the array of arrays into an array of objects, use map:
const data = [
["baseball","Noun","Singular","","","","",""],
["foo","bar","baz","","","","",""]
];
const transformed = Object.assign(...data.map(([prop, value1, value2]) => ({ [prop]: [value1, value2] })));
console.log(transformed);
A bit simpler compared to other answers here but works as well.
const data = [
["baseball","Noun","Singular","","","","",""],
["baseball1","Noun1","Singular1","","","","",""],
["baseball2","Noun2","Singular2","","","","",""]
];
const obj = [];
data.forEach(function(i) {
let jsonObj = {};
jsonObj [i[0]] = i.slice(1).filter(x=>x !='');
obj.push(jsonObj)
});
console.log(JSON.stringify(obj))
Just using forEach, considering multiple array elements.
var obj = {};
var arr = [
["baseball", "Noun", "Singular", "", "", "", "", ""],
["Test", "Test1", "Test2", "", "", "", "", ""]
];
arr.forEach(function(val, idx) {
val.forEach(function(val1, idx1) {
if (idx1 === 0) {
obj[val1] = val.slice(1, val.length)
}
})
})
console.log(JSON.stringify(obj))