im struggling with following logic.
I am creating following array of objects upon running through a given string.
[{"word":"Frank","c":1},
{"word":"Irina","c":1},
{"word":"Frank","c":1},
{"word":"Frank","c":1},
{"word":"Thomas","c":1}]
what i want to achieve is:
[{"word":"Frank","c":3},
{"word":"Irina","c":1},
{"word":"Thomas","c":1}]
what would be the best way here?
I am sending the string to this function and create the array. but im not able to get what I want.
function words(str) {
return str.split(" ").reduce(function(count, word) {
if(word.length>2&&isLetter(word)){
data.push({word:word, count: 1});
}
}, {});
}
thanks for some help
Adrian
You can use object accumulator to keep count of each word and then using Object.values() get all the values.
function words(str) {
return Object.values(str.split(" ").reduce((result, word) => {
result[word] ??= {word, count: 0};
result[word].count += 1;
return result;
}, {}));
}
console.log(words("Frank Irina Frank Frank Thomas"));
.as-console-wrapper { max-height: 100% !important; top: 0; }
Assuming that c is not always 1 (in which case you can do as the other post suggested and just keep count of the words), you can do it like this, looping through the data and summing the c values in a buffer, using word as the key for the buffer. Then map the buffer values to a final array.
const data = [{"word":"Frank","c":1},
{"word":"Irina","c":1},
{"word":"Frank","c":1},
{"word":"Frank","c":1},
{"word":"Thomas","c":1}];
//console.log(data);
let buffer = {};
data.forEach(i=>{
let w = i.word;
let words = buffer[w] || {word: w, c: 0};
words.c = i.c*1 + words.c;
buffer[w] = words;
});
let final = Object.values(buffer).map(b=>{
return {word: b.word, c: b.c};
});
console.log(final);
This will work for any values of c:
const data = [{"word":"Frank","c":2},
{"word":"Irina","c":4},
{"word":"Frank","c":1},
{"word":"Frank","c":3},
{"word":"Thomas","c":1}];
//console.log(data);
let buffer = {};
data.forEach(i=>{
let w = i.word;
let words = buffer[w] || {word: w, c: 0};
words.c = i.c*1 + words.c;
buffer[w] = words;
});
let final = Object.values(buffer).map(b=>{
return {word: b.word, c: b.c};
});
console.log(final);
Related
I have a string
var str = "1:6,5,2,2:3";
I want to convert this str into a js dictionary such that:
var dict = {1:"6,5,2",
2:"3"};
so that I can fetch the values by their respective key index. How do I convert it?
I had tried this code to store the splitted values into an array:
var pages = "1:6,5,2,2:3";
var numbers = [];
if (pages.includes(',')) {
page_nos = pages.split(',');
for (var i = 0; i < page_nos.length; i++) {
if (page_nos[i].includes(':')) {
var n = page_nos[i].split(':');
numbers.push(n[1]);
} else {
numbers.push(page_nos[i]);
}
}
} else {
page_nos = pages.split(':');
numbers.push(page_nos[1])
};
console.log('numbers: ', numbers);
But it's incorrect, as without dictionary it's impossible to know what value belongs to which index
If you cannot make your input string a proper JSON or another easily parsable format in the first place, this answers your question:
const str = "1:6,5,2,2:3";
const obj = str.split(/,(?=\d+:)/).reduce((accu, part) => {
const [k, v] = part.split(':', 2);
accu[k] = v;
return accu;
}, {});
console.log(obj);
Cut the string at all commas that are followed by digits and a colon. Each part has a key in front of a colon and a value after it, which should be stuffed in an object in this format.
No mutations solution.
const str = "1:6,5,2,2:3";
const dict = str
.split(/(\d+:.*)(?=\d+:)/g)
.reduce((t, c) => {
const [key, value] = c.replace(/,$/, "").split(/:/);
return { ...t, [key]: value }
});
console.log(dict);
if you consider not using regular expression, you might try this as well.
to take out a dict (Object) from that string, this will do.
var pages = "1:6,5,2,2:3";
function stringToObject(str) {
var page_object = {};
var last_object;
str.split(",").forEach((item) => {
if (item.includes(":")) {
page_object[item.split(":")[0]] = item.split(":")[1];
last_object = item.split(":")[0];
} else {
page_object[last_object] += `,${item}`;
}
});
return page_object;
}
console.log(stringToObject(pages))
Presented below may be one possible solution to achieve the desired objective.
NOTE:
In lieu of var the code uses either let or const as applicable.
Code Snippet
const pages = "1:6,5,2,2:3";
const resObj = {};
let page_nos, k;
if (pages.includes(',')) {
page_nos = pages.split(',');
for (let i = 0; i < page_nos.length; i++) {
if (page_nos[i].includes(':')) {
let n = page_nos[i].split(':');
k = n[0];
resObj[k] = n[1].toString();
} else {
resObj[k] += ", " + page_nos[i].toString();
}
}
} else {
page_nos = pages.split(':');
resObj[page_nos[0]] = [page_nos[1]]
numbers.push(page_nos[1])
};
console.log('result object: ', resObj);
This code essentially fixes the code given in the question. It is self-explanatory and any specific information required may be added based on questions in comments.
You could take nested splitring for entries and get an object from it.
const
str = "1:6,5,2,2:3",
result = Object.fromEntries(str
.split(/,(?=[^,]*:)/)
.map(s => s.split(':'))
);
console.log(result);
I have the following JSON:
{
"hominis": [20000, "asd"],
"omint": [30000, "asd"]
}
and I would like to make a function that returns all array names (hominis, omint) whose arr[0] value is under my input value
e.g:
if (myInput <= arr[0]) {
return arrName
}
I would like to go through my arrays (hominis, omint) and return their names if the condition is matched. I believe it has something to do with for loops, but I couldn't do it.
I've started JS two weeks ago so I'm a newbie.
Thank you
You can iterate over the keys of an object like following snippet,
var a = {
"hominis": [20000, "asd"],
"omint": [30000, "asd"]
};
var minValue = 25000;
var keys = Object.keys(a); // Returns all keys in the object
for (let key of keys) { // Loop over all the keys
if (a[key][0] > minValue) { // Check if value at key matches your condition
console.log(key); // return/add to your new array/ or whatever to the matching key
}
}
You did not say how to show your output. I am assuming it will be array of names.
let data = {
hominis: [20000, "asd"],
omint: [30000, "asd"],
};
myInput = 20001;
let x = Object.entries(data).reduce((p, [x, y]) => {
if (myInput <= y[0]) p.push(x);
return p;
}, []);
console.log(x);
You can use "for in obj".
var obj1 = {
"hominis": [20000, "asd"],
"omint": [30000, "asd"]
}
function findMinorThan(obj, limit) {
let res = []
for (const k in obj) {
if (obj[k][0] < limit) {
res.push(k)
}
}
return res
}
console.log(findMinorThan(obj1, 25000));
I use JavaScript and Chartjs library to draw charts. I store points data in JSON:
"Chart1":{"1": "4","4": "4","10": "4"}
To draw charts correctly I need to provide input like that:
data:[{x:1,y:4},{x:4,y:4},{x:10,y:4}].
I tried do this this way:
var userData = JSON.parse('{"Chart1":{"1": "4","4": "4","10": "4"}}');
var MyKey = [Object.keys(userData['Chart1'])];
var MyVal = [Object.values(userData['Chart1'])];
var POINTS = []; //I tried also {}
for(b=0; b <= MyKey.length; b++)
{
POINTS.push({x:MyKey[b],y:MyVal[b]});
}
and then in config variable:
data:[POINTS]
but the output is:
[
0:
{
x:["1","4","10"]
y:["4","4","4"]
}
1:
{
x:undefined
y:undefined
}
}
f
So how I should do this correctly?
EDIT:
I corrected json string, I pasted it wrong, it's only a typo but thank you all for vigiliance
You can use Object.entries()
var userData = JSON.parse('{ "Chart1":{"1": "4","4": "4","10": "4"} }');
const data = Object.entries(userData['Chart1']).map(([x, y]) => ({ x, y }));
console.log(data);
Some characters were missing from the json string in your example.
Object.keys and Object.values return arrays, don't enclose them in [].
You're storing strings, but your points probably need numbers for x and y. You can do the conversion with a + operator (or parseInt or Number).
var userData = JSON.parse('{"Chart1":{"1": "4","4": "4","10": "4"}}');
var MyKey = Object.keys(userData['Chart1']);
var MyVal = Object.values(userData['Chart1']);
var POINTS = [];
for (b = 0; b < MyKey.length; b++) {
POINTS.push({
x: +MyKey[b],
y: +MyVal[b]
});
}
console.log(POINTS);
You can do this by using Object.entries to get the keyPair value and then map it so that x = key and y = value.
const x = {Chart1:{"1": "4","4": "4","10": "4"}};
const chartKeyValuePair = Object.entries(x.Chart1);
const xy = chartKeyValuePair.map(([key, value]) => {return {x:key, y: value}});
console.log(xy);
You're overcomplicating it! If you use a for...in loop, you get the key of each property in the data, and you can use that to refer to the value. No need for Object.keys etc. Demo:
var userData = JSON.parse('{"Chart1":{"1": "4","4": "4","10": "4"}}');
var POINTS = [];
for (key in userData.Chart1) {
POINTS.push({
x: key,
y: userData.Chart1[key]
});
}
console.log(POINTS);
N.B. You'll see I made an assumption about the real stucture of the userData JSON string, because what you've posted in the question isn't valid JSON and causes a script error.
I have an array of objects like :
a = [{"a":1,"b":2},{"a":3,"c":5}]
I would like to obtain, in an efficient way :
b = [{"a":1,"b":2,"c":"blabla"},{"a":3,"b":"blabla","c":5}]
My function so far is (using the underscoreJS library in the first line):
let fillFuncArr = (res,fill) => {
// we fill the potential missing values
let col_names = _.uniq(res.map(x => Object.keys(x)).reduce((acc, curr) => acc.concat(curr), []), false);
for (let i = 0; i < res.length; i++) {
for (let j = 0; j < col_names.length; j++) {
if (!res[i].hasOwnProperty(col_names[j])) {
res[i][col_names[j]] = fill;
}
}
}
return res;
};
In my example above you would do :
b = fillFuncArr(a,"blabla")
How to make that function faster if at all possible ?
===========================================================================
EDIT after answers to benchmark replies :
I tested the functions like this :
for (let i=0;i<num_tries;i++) {
let time_tmp = performance.now();
console.time("old");
fillFuncArr(obj,"blah");
console.timeEnd("old");
time_old.push(performance.now()-time_tmp);
time_tmp = performance.now();
console.time("new");
fillFuncArrNew(obj,"blah");
console.timeEnd("new");
time_new.push(performance.now()-time_tmp);
}
This answer (the first calculation of the old function is always much faster than the subsequent ones, not quite sure why...) is 50-100 times faster. The fill time is the same, it's getting the keys that makes up all the speed gains :
"old": [
147.52006196975708,
1065.4309248924255,
1023.5124139785767,
1021.830512046814,
1855.5670911073685,
1006.7114781141281,
996.8541929721832,
1306.3085260391235
],
"new": [
18.814231991767883,
23.46549105644226,
17.708116054534912,
15.55942702293396,
18.764864921569824,
15.866382002830505,
19.18179702758789,
23.987511038780212
]
Don't know if this is faster, but definitely shorter:
dummy = {a: 'dummy', b: 'dummy', c: 'dummy'}
a = [{"a": 1, "b": 2}, {"a": 3, "c": 5}]
r = a.map(x => ({...dummy, ...x}))
console.log(r)
If you want the dummy to be fully dynamic, then
function fillKeys(a, value) {
let keys = new Set(a.flatMap(Object.keys)),
dummy = Object.fromEntries(
[...keys].map(k => [k, value]));
return a.map(x => ({...dummy, ...x}));
}
//
a = [{"a": 1, "b": 2}, {"a": 3, "c": 5}]
r = fillKeys(a, 'dummy')
console.log(r)
That being said, I'm sure this is actually an XY problem, so it would help to explain us what you're actually doing. For example, if you just want all objects in the list to respond to the same set of keys, that would be much easier (and also faster) with proxies.
First figure out all keys in the array items, create a "default" object filled with the fill as its values, then iterate over the items and spread the default object, then the original object:
const fillFuncArr = (arr, fill) => {
const allKeys = new Set(arr.flatMap(Object.keys));
const defaultObj = {};
for (const key of allKeys) {
defaultObj[key] = fill;
}
return arr.map(obj => ({ ...defaultObj, ...obj }));
};
a = [{"a":1,"b":2},{"a":3,"c":5}]
b = fillFuncArr(a,"blabla")
console.log(b);
Take a look here:
console.time('allTest');
var was = [{a:1, b:2}, {a:3, c:5}];
function blah(array){
var a = [], o, b = 'blabla';
array.forEach(function(w){
o = {};
o.a = 'a' in w ? w.a : b;
o.b = 'b' in w ? w.b : b;
o.c = 'c' in w ? w.c : b;
a.push(o);
});
return a;
}
console.time('test'); console.log(blah(was)); console.timeEnd('test'); console.timeEnd('allTest');
At least this will show you how to test the time.
const a = [{"a":1,"b":2},{"a":3,"c":5}];
const allKeys=[...new Set(a.flatMap(Object.keys))]
const dummyObj=Object.fromEntries(allKeys.map(key => [key, 'blabla']));
console.log(a.map(data => ({...dummyObj, ...data})))
I just started getting into RegEx. So far I have something like this but it looks like my pattern is off.
function hasRepeatedLetters(str) {
var patt = /[a-zA-Z]/g;
var result = patt.exec(str);
return result;
}
console.log(hasRepeatedLetters('aaBcDDbcca'));
I'd like to be able to input any argument with a combo of letters and the output to be a two-dimensional array with the first value being the repeated letter and the second value being the repeated letters length.
[["a",3],["b",1],["B",1],["c",3],["D",2]]
Here is a map / object based solution
var str = 'aaBcDDbcca';
var map = {}; // create a hash for keys and their counts
// loop over all characters and then add count
Array.from(str).forEach(function(character) {
map[character] = map[character] ? map[character] + 1 : 1;
});
// finally, convert map to a 2D array with 1st item as key and second item as count
var counts = Object.keys(map).map(function(key) {
return [key, map[key]];
})
console.log(counts);
You could use an ES6 Map for this, which has some nice features:
a map's set method returns the map again
the spread syntax applied on a map produces the desired array of pairs
function hasRepeatedLetters(str) {
return [...[...str].reduce( (acc, c) => acc.set(c, (acc.get(c) || 0) + 1), new Map )];
}
console.log(hasRepeatedLetters('aaBcDDbcca'));
.as-console-wrapper { max-height: 100% !important; top: 0; }
Possible solution, using Array#forEach function.
var str = 'aaBcDDbcca'.split(''), obj = {};
str.forEach(v => obj[v] ? obj[v]++ : obj[v] = 1);
var res = Object.keys(obj).map(v => [v, obj[v]]);
console.log(JSON.stringify(res));
Personally, I would suggest you to keep it as an object.
var str = 'aaBcDDbcca'.split(''), obj = {};
str.forEach(v => obj[v] ? obj[v]++ : obj[v] = 1);
console.log(JSON.stringify(obj));