I have a string in the form
Key=asdf, num=90, Key=ert, num=20, Key=yged, num=20, Key=kned, num=35
I have to filter only Key num pairs which has value 20 and store them into a Key Value pair such that Key=ert, num=20 will be first record and Key=yged, num=20 will be second record so on. How can I use Map in JavaScript so that always first value will go as key and second will go as value and form pairs in this case. I have used the following :
var dataString = JSON.parse(data).data;
var arrayVal = new Array();
arrayVal = dataString.split(', ');
for(a in arrayVal){
console.log(arrayVal[a]);
}
Array.map probably isn't the best tool for the job here. Array.reduce would be a better approach. Since you know what your delimiters are (namespaces and equal signs), you can logically split things up so that you know that every other iteration will give you a key/value. You can then create a mechanism to track what the last key is so you can map the value to it.
const str = 'Key=asdf, num=90, Key=ert, num=20, Key=yged, num=20, Key=kned, num=35';
const arr = str.split(', ').reduce( (acc, curr) => {
const entry = curr.split('=');
const key = entry[0];
const val = entry[1];
if (key === 'Key') {
acc['last_key'] = val
acc[val] = null;
} else if (key === 'num') {
acc[acc['last_key']] = val;
}
return acc;
}, {});
delete arr.last_key;
console.log(arr);
Here you go. It's kinda ugly.
let result = dataString.split(', ')
.map((value, index, array) =>
value === 'num=20' && array[index - 1])
.filter(x => x);
console.log(result);
Here's my say
const dataString = JSON.parse(data).data;
const arrayVal = dataString.split(', ');
const obj = {}; // initialize the object
for (let i = 0; i < arrayVal.length; i += 2) { // Iterating over two elements for the key and value
const key = arrayVal[i].split('=')[1];
const value = arrayVal[i + 1].split('=')[1];
obj[key] = value;
}
console.log(obj);
Related
Task: You have array with key: value pairs like:
arr = ["x:1", "y:2", "a:5", "x:5", "z:0"]
You must sum all the same keys so it will be:
["x:6","y:2","a:5","z:0"]
on output.
I finish this task but its complicate. I have solved the task but I am looking for a simpler solution
var a = ["x:1", "y:2", "a:5", "x:5", "z:0"]
function saberi(niz) {
let povratni = [];
for (clan of niz) {
let razdvojen = clan.split(":");
let key = razdvojen[0]
let value = razdvojen[1]
let provjera = false
for (let i = 0; i < povratni.length; i++) {
let razdvojen2 = povratni[i].split(":")
let key2 = razdvojen2[0]
let value2 = razdvojen2[1]
if (key2 == key) {
console.log("uso")
let novavrijednost = parseFloat(value) + parseFloat(value2)
povratni[i] = key2 + ":" + novavrijednost
provjera = true
break
}
}
if (!provjera) {
povratni.push(clan)
}
}
console.log(povratni)
return povratni
}
saberi(a)
You can use objects to make this easier. Each character to the left of the colon : will be a key in the object, and the number to the right of the colon will be the value, which you can accumulate to the value from the object at the given key.
To start, you can loop through your arr using a for...of loop, and for each string in your array, use .split(":") to break the string into its key and value components. You can then grab the current value stored at the key from the object (grouped) by using grouped[key]. If the value doesn't exist, you can default it to 0 using || 0. Once you have the current value stored in the object, you can add the number version of the value to the accumulated value, and store that new value in the object at the key key.
Once you have your object, you can grab the entries to get an array of the form [[key, value], [key2, value2], ...], which you can use .map() on to convert each [key, value] to a string of the form key:value:
const arr = ["x:1", "y:2", "a:5", "x:5", "z:0"];
const grouped = {};
for(const str of arr) {
const [key, num] = str.split(":");
grouped[key] = (grouped[key] || 0) + Number(num);
}
const res = Object.entries(grouped).map(entry => entry.join(":"));
console.log(res);
The same concept above can be accomplished using .reduce() with a Map instead of an object, as well as Array.from to map the entries of the Map to an array:
const arr = ["x:1", "y:2", "a:5", "x:5", "z:0"];
const res = Array.from(arr.reduce((map, str) => {
const [key, val] = str.split(":");
return map.set(key, (map.get(key) || 0) + +val);
}, new Map), entry => entry.join(':'));
console.log(res);
You could use Array.prototype.reduce() method. Traverse the array and split each item by colon(:). Then group it by key to count sum. At last, transform the object into a key-value pair array using Object.entries() method and then map and join it by colon(:).
const arr = ['x:1', 'y:2', 'a:5', 'x:5', 'z:0'];
const ret = Object.entries(
arr.reduce((prev, c) => {
const p = prev;
const [k, v] = c.split(':');
p[k] = p[k] ?? 0;
p[k] += +v;
return p;
}, {})
).map((x) => x.join(':'));
console.log(ret);
What I have:
var test ='1=Car&2=Bike&10=rabbit&10=dog&10=horse&11=ferrari&11=mercedes';
is a string, which I split and convert to array. I want that for every value that contains the same starting number, they get merged into the same value.
Example, the string above becomes tha array I don't want:
[ "1=Car", "2=Bike", "10=rabbit", "10=dog", "10=horse", "11=ferrari", "11=mercedes" ]
What I want, instead:
[ "1=Car", "2=Bike", "10=rabbit,dog,horse", "11=ferrari,mercedes" ]
My actual code:
var test ='1=Car&2=Bike&10=rabbit&10=dog&10=horse&11=ferrari&11=mercedes';
var array = test.split('&');
console.log(array);
var check_multiselect = null;
var current_multiselect = null;
for (const [key, value] of Object.entries(array)) {
var obj = value.split('=');
if (obj[0] == check_multiselect) {
console.log(current_multiselect);
current_multiselect = key - 1;
array[current_multiselect] = array[current_multiselect] +', '+obj[1];
}
check_multiselect = obj[0];
};
console.log(array);
Which does not work as expected. What's wrong in there?
You could find same starting number and update the value.
var test ='1=Car&2=Bike&10=rabbit&10=dog&10=horse&11=ferrari&11=mercedes',
result = test
.split('&')
.reduce((r, string) => {
let [key, value] = string.split('='),
index = r.findIndex(q => q.split('=')[0] === key);
if (index === -1) r.push(string);
else r[index] += ',' + value;
return r;
}, []);
console.log(result);
I have two arrays containing some parameter values. All elements in the arrays are strings like the following:
x = [ "vorzugsreihe=J", "nennleistung=94,1127", "nenndrehzahl=31,9400"]
y = ["nenndrehzahl=500,3000"]
Expected Output would be:
x = [ "vorzugsreihe=J", "nennleistung=94,1127", "nenndrehzahl=500,3000"]
I have tried using Array.Filter but can't seem to be able to filter only partially (like starting with the string instead of the whole string since that won't match as the values are different).
What I'd like is to be able to go through each element from array Y, and search if the element(string before "=") exists in array X and replace the value(s) of that element in array X.
for(var i=0;i<x.length;i++){
var currentStr = x[i];
var currentInterestedPart = /(.+)=(.+)/.exec(currentStr)[1];
var replacePart = /(.+)=(.+)/.exec(currentStr)[2];
for(var j=0;j<y.length;j++){
if(!y[j].startsWith(currentInterestedPart)) {continue;}
var innerReplacePart = /(.+)=(.+)/.exec(y[j])[2];
x[i] = currentStr.replace(replacePart,innerReplacePart);break;
}
}
Try this. This makes use of RegEx and it is less error prone.
You can use Map and map
First create a Map from array y, split each element by = use first part as key and second part as value
Loop over x array, split each element by = and use first part as key to search in Map if it's present use value from Map else return without any change
let x = ["vorzugsreihe=J", "nennleistung=94,1127", "nenndrehzahl=31,9400"]
let y = ["nenndrehzahl=500,3000"]
let maper = new Map(y.map(v => {
let [key, value] = v.split('=', 2)
return [key, value]
}))
let final = x.map(v => {
let [key, value] = v.split('=', 2)
if (maper.has(key)) {
return key + '=' + maper.get(key)
}
return v
})
console.log(final)
For each value in the y array, iterate and check if the word exist in the x array. Once you find a match just update the value. (The below solution mutates the original array)
const x = [ "vorzugsreihe=J", "nennleistung=94,1127", "nenndrehzahl=31,9400"],
y = ["nenndrehzahl=500,3000"],
result = y.forEach(word => {
let [str, number] = word.split('=');
x.forEach((wrd,i) => {
if(wrd.split('=')[0].includes(str)) {
x[i] = word;
}
});
});
console.log(x);
I'd suggest using combination of reduce + find - this would accumulate and give you the results you're expecting.
var x = [ "vorzugsreihe=J", "nennleistung=94,1127", "nenndrehzahl=31,9400"]
var y = ["nenndrehzahl=500,3000"]
var combinedArr = x.reduce((acc, elem, index) => {
const elemFoundInY = y.find((yElem) => yElem.split("=")[0] === elem.split("=")[0]);
if (elemFoundInY) {
acc = [...acc, ...[elemFoundInY]]
} else {
acc = [...acc, ...[elem]];
}
return acc;
}, [])
console.log(combinedArr);
You can use .startsWith() to check if element start with key= and then replace its value:
let x = [ "vorzugsreihe=J", "nennleistung=94,1127", "nenndrehzahl=31,9400"];
let y = ["nenndrehzahl=500,3000"];
y.forEach(val => {
let [key, value] = val.split("=");
for (let i = 0; i < x.length; i++) {
if (x[i].startsWith(`${key}=`)) x[i] = `${x[i].split("=")[0]}=${value}`;
}
})
console.log(x)
Try this:
y.forEach(item => {
const str = item.split("=")[0];
const index = x.findIndex(el => el.startsWith(str));
if (index) {
const split = x[index].split('=');
x[index] = `${split[0]}=${split[1]}`;
}
})
I have a string:
var rrule = "DTSTART=20190514T111500Z;FREQ=DAILY;INTERVAL=1";
I want to convert this string to key-> value pairs in an array.
[
dtstart: 20190514T111500Z,
freq: daily,
interval: 1
]
I know I can take the string and split it based on the semicolon:
var array = rrule.split(";");
... but this leaves me with an array like this:
[
"DTSTART=20190514T111500Z",
"FREQ=DAILY",
"INTERVAL=1"
]
I guess I need another step to map out the keys/values, but I get lost at this point.
Ideally, for the string I want to be able to easily access what dtstarts equals, what interval equals, what other variables equal and so on.
let str = "DTSTART=20190514T111500Z;FREQ=DAILY;INTERVAL=1";
let obj = {};
for (let entry of str.split(";")) {
let pair = entry.split("=");
obj[pair[0]] = pair[1];
}
console.log(obj);
You already know how to split on the ; to get an array, from there you can just aggregate (using reduce) to get an object:
var rrule = "DTSTART=20190514T111500Z;FREQ=DAILY;INTERVAL=1";
var result = rrule.split(";").reduce( (obj,item) => {
let [key,value] = item.split("=");
obj[key] = value;
return obj;
},{});
console.log(result["DTSTART"])
console.log(result["FREQ"])
console.log(result["INTERVAL"])
You were correct to start with split first, this would then return you an array of strings.
To easily convert them, just use map, to return the split the single strings once more, and then return an object based on the property name you would like to give it and it's value
function createKeyValuePairFromString( str ) {
return str.split(';').map( item => {
const splitted = item.split('=');
return { [splitted[0]]: splitted[1] };
});
}
console.log( createKeyValuePairFromString("DTSTART=20190514T111500Z;FREQ=DAILY;INTERVAL=1") );
Use array created and split it again with =
function convertToObject(cookieString) {
const cookieObj = {};
if (!cookieString && typeof cookieString !== 'string') return cookieObj;
const arr = cookieString.split(';');
arr.forEach(record => {
if (record.includes('=')) {
const [key, value] = record.split('=');
cookieObj[key.trim()] = value;
}
});
return cookieObj;
}
You can use it like the code below:
var rrule = "DTSTART=20190514T111500Z;FREQ=DAILY;INTERVAL=1";
let finalObj = {};
rrule.split(';').forEach(i => finalObj[i.split('=')[0]] = i.split('=')[1]);
console.log('finalObj',finalObj);
Here I'm first splitting with ';' so consider the first item to be DTSTART=20190514T111500Z Then on splitting with = I get finalObject['DTSTART'] = 20190514T111500Z
Using forEach()
let str = "DTSTART=20190514T111500Z;FREQ=DAILY;INTERVAL=1";
let obj = {};
let strArr = str.split(';')
strArr.forEach((str) => {
let [key, value] = str.split('=')
obj[key] = value;
});
console.log(obj);
Here's a fairly simple version, returning an object, not an array:
const toObj = str => str
.split (';')
.map ( s => s .split ('=') )
.reduce ( (a, [k, v]) => ({...a, [k]: v}), {} )
let str = "DTSTART=20190514T111500Z;FREQ=DAILY;INTERVAL=1";
console.log (
toObj(str)
)
One of the reasons I like the library is that we can write this sort of logic more simply. In Ramda (disclaimer: I'm one of the authors), it might look like this:
const toObj = pipe ( split (';'), map (split ('=') ), fromPairs)
let str = "DTSTART=20190514T111500Z;FREQ=DAILY;INTERVAL=1";
console.log (
toObj(str)
)
<script src="https://bundle.run/ramda#0.26.1"></script><script>
const {pipe, split, map, fromPairs} = ramda; </script>
var str = "DTSTART=20190514T111500Z;FREQ=DAILY;INTERVAL=1";
// string splitting rule
const rule = (string, delimiter) => string.split(delimiter);
const result = rule(str, ';').reduce((acc, s) => {
const [key, value] = rule(s, '=');
acc[key] = value;
return acc;
}, {});
console.log(result);
I believe what I need are two JavaScript functions. I am receiving a comma separated string that holds two types of data: 1) device name followed by 2) numeric value. These two values are separated by a comma, and each set is also separated by a comma. Example string below:
Device_A,5,Device_C,2,Device_A,10,Device_B,8,Device_B,2,Device_C,7
What I want to do is create two separate functions. The first function finds the unique device names and returns just the names in a comma separated string. The second function would calculate the sum of the numeric values for each device. The expected results from the example string above would return:
Function 1 (Device List):
Device_A, Device_B, Device_C
Function 2 (Sums per Device List):
15,10,9
The lists do not need to return in any particular order as long at they both match up. All I have successfully done at this point is return a list of unique values (including numeric values)... I'm stuck on separating the list, but still referring to device name to sum up all of the values.
Thanks in advance. Let me know if you have any questions!
Matt
You could use an object for collecting the names and count.
This edit contains a shared function and two function for the result in equal order.
function getGrouped(data) {
var array = data.split(','),
temp = Object.create(null),
i = 0;
while (i < array.length) {
temp[array[i]] = (temp[array[i]] || 0) + +array[i + 1] || 0;
i += 2;
}
return temp;
}
function getDevices(data) {
var temp = getGrouped(data);
return Object.keys(temp).sort().join();
}
function getCounts(data) {
var temp = getGrouped(data);
return Object.keys(temp).sort().map(function (k) { return temp[k]; }).join();
}
var data = "Device_A,5,Device_C,2,Device_A,10,Device_B,8,Device_B,2,Device_C,7";
console.log(getDevices(data));
console.log(getCounts(data));
When starting out on a problem like this, I think it's wise to not worry about doing it in a single loop or in a fancy one-liner at first.
A) Start out by defining what data structures you need and how to go from one format to another:
Convert my string of data to a list of keys and values
Somehow group these keys and values based on the key
Sum the values for each group
Return a list of all unique keys
Return a list of all summed values
B) Then, try to see if any of the code you've written has the potential be re-used by other parts of your application and refactor accordingly.
C) Finally, assess if there are performance bottle necks and only if there are, optimize for performance.
A. A function for each step:
// 1. From string to array of keys and values
// You already figured this one out. Split by ","!
const namesAndValuesFromString =
str => str.split(",");
// 2. Grouping by key
// Let's first make pairs:
const deviceValuePairs = devicesAndValues => {
let pair = [];
const pairs = [];
devicesAndValues.forEach(x => {
pair.push(x);
if (pair.length === 2) {
pairs.push(pair);
pair = [];
}
});
return pairs;
};
// Key value pairs are a nice starting point for constructing a grouped object:
const kvpsToDeviceValuesObj = kvps => {
const valuesByDevice = {};
kvps.forEach(([key, value]) => {
value = Number(value);
if (!valuesByDevice[key]) {
valuesByDevice[key] = [];
}
valuesByDevice[key].push(value);
});
return valuesByDevice;
};
// 3. Now, we can get to summing the values arrays
const sumValueArrays = valuesByDevice => {
const summedValuesByDevice = {};
// Loop over the objects entries
Object.entries(valuesByDevice).forEach(
([key, values]) => {
summedValuesByDevice[key] = values
.reduce((a, b) => a + b);
}
);
return summedValuesByDevice;
};
// 4. + 5. Now that we have an object with device ids as keys, and summed values inside, we can retrieve the two lists
const getDevices = Object.keys;
const getSums = Object.values;
// Running the code:
const namesAndValues =
namesAndValuesFromString("A,5,C,2,A,10,B,8,B,2,C,7");
console.log(namesAndValues);
const kvps = deviceValuePairs(namesAndValues);
console.log(kvps);
const valuesByDevice = kvpsToDeviceValuesObj(kvps);
console.log(valuesByDevice);
const sumValues = sumValueArrays(valuesByDevice);
console.log(sumValues);
const devices = getDevices(sumValues);
console.log(devices);
const sums = getSums(sumValues);
console.log(sums);
B. Refactoring!
Once you understand each of those steps, you'll start to see things that can be generalized or combined. That's where the fun starts :)
// UTILITIES
const split = del => arr => arr.split(del);
const toPairs = arr => {
let pair = [];
return arr.reduce(
(pairs, x) => {
pair.push(x);
if (pair.length === 2) {
pairs.push(pair);
pair = [];
}
return pairs;
}, []);
};
const sum = (x, y = 0) => +x + y;
const kvpsToGroups = grouper => kvps =>
kvps.reduce(
(groups, [key, value]) => Object.assign(groups, {
[key]: grouper(value, groups[key])
}), {});
// YOUR APP
const sumGrouper = kvpsToGroups(sum);
const dataSplitter = split(",");
const parseData = str => sumGrouper(toPairs(dataSplitter(str)));
// MAIN
const result = parseData("A,5,C,2,A,10,B,8,B,2,C,7");
console.log("devices:", Object.keys(result));
console.log("sums:", Object.values(result));
another way by regexs
let str = "Device_A,5,Device_C,2,Device_A,10,Device_B,8,Device_B,2,Device_C,7", obj = {}
str.match(/(\w+,[0-9]+)/g).forEach((s) => {
s = s.split(',')
obj[s[0]] = (obj[s[0]] || 0) + (Number(s[1]) || 0)
})
console.log(obj)
Something like this should do it:
var input = "Device_A,5,Device_C,2,Device_A,10,Device_B,8,Device_B,2,Device_C,7";
var output = input.split(',').reduce((accumulator, currentValue, currentIndex, array) => {
accumulator[currentValue] = (accumulator[currentValue] || 0)
+ parseInt(array[currentIndex + 1]);
array.splice(0,1);
return accumulator;
}, {});
console.log(Object.keys(output));
console.log(Object.keys(output).map(k => output[k]));