Assign conditions/categories to elements in array - javascript

I have an array of words - my stimuli - and they are presented on the screen. Yet, each word has another "condition", that is they are either, say categoryA, categoryB, or categoryC. This is probably simple, but I couldn't find an answer and am stuck with it.
My final aim is to assign the categories randomly to the stimuli each time the script is run.
var stim = [ "AEROPLANE", "TRAIN", "CAR", "BIKE", "WALKING"];
Here I'd like AEROPLANE to have categA, TRAIN categB, and the rest categC.
I thought about something like this (or with integers instead of Letters):
var stim = [ ["AEROPLANE", A], ["TRAIN", B], ["CAR", C], ["BIKE", C], ["WALKING",C] ];
But this doesn't work. If I have categories, how would I access them to code responses?
This is the script that presents the stimuli (on each keypress a new one):
$(function(){
$(document).keypress(function(e){
if ($(e.target).is('input, textarea')) {
return;
};
if (e.which === 97 || e.which === 108) {
new_word = w_stim[Math.floor(Math.random() * stim.length)];;
$("#abc").text(new_word);
};
});
});

Make an array of objects:
var stim = [
{name:"AEROPLANE", category:"A"},
{name:"TRAIN", category:"B"},
{name:"CAR", category:"C"},
{name:"BIKE", category:"C"},
{name:"WALKING",category:"C"}
];
Then access the objects like:
stim[i].name
stim[i].category
JS Fiddle: http://jsfiddle.net/pJ6X2/

Another option would be
var stim = {
'A': ['AEROPLANE'],
'B': ['TRAIN'],
'C': ['CAR', 'BIKE', 'WALKING']
}
var items = stim[categoryChar];
if (undefined === items)
console.log('no such category');
else
console.log(items[Math.floor(Math.random() * items.length)]);

Related

How does one detect and access numbered property names like 'entry1', 'entry2'?

A data structure similar to the following provided ...
{
strIngredient1: 'Olive Oil',
strIngredient2: 'Onion',
strIngredient3: 'Chicken Breast',
strMeasure1: '',
strMeasure2: '',
keyWithNoIndexCount: '',
}
... needs to be processed.
How could one use a loop in order to generate the number count within keys like strIngredient1, strIngredient2, ... and so on?
I am having trouble working out which terms to search for. I want to use a for loop so each number after strIngredient would be i.
I want to grab each entry like that ... strIngredient + i ... and add them to an array but I do not know how to access them like this, if it is even possible or the best way. I do not know how to link the two parts of the following pseudo code ...
const ingredList = [];
for(i = 1; i < 20; i++) {
ingredList.push(response.meals[0].strMeasure ? );
ingredList.push(response.meals[0].strIngredient ? );
}
console.log(ingredList);
Edit 1
There are also additional but non related entries in the data-structure.
Edit 2
There are also additional keys like strMeasure1, strMeasure2, strMeasure3 etc. I want to access them too in addition to the already mentioned strIngredient based ones.
You can achieve that in two ways:
A: Grab values of all keys, regardless of key:
const jsonObj = {
"strIngredient1": "Olive Oil",
"strIngredient2": "Onion",
"strIngredient3": "Chicken Breast"
};
let ingredListA = Object.keys(jsonObj).map(key => jsonObj[key]);
console.log('ingredListA:', ingredListA);
C: Grab values of by named & numbered keys:
const jsonObj = {
"strIngredient1": "Olive Oil",
"strIngredient2": "Onion",
"strIngredient3": "Chicken Breast"
};
let ingredListB = [];
for(let i = 1; i <= 3; i++) {
let key = 'strIngredient' + i;
ingredListB.push(jsonObj[key]);
}
console.log('ingredListB:', ingredListB);
If needed you could add a 3rd solution that is a hybrid of A and B: Loop through all object keys, use value only of key matches desired pattern.
The OP needs a generic approach in order to parse from any given property-name the constant key part and the serial index. Like with a property name of 'strIngredient2' where the constant key is 'strIngredient' and the index is '2'.
One would achieve this via the usage of a regex like /(?<key>.*?\D)(?<index>\d*$)/ which features named capturing groups.
The regex-based parsing is part of a callback-function which does reduce the entries of the OP's provided object.
In order to stay even more generic by taking into account the renaming of some or all of the original object's property-names one would provide an object-based initial (reduce) value which features both,
a lookup for renaming some or all of the original property-names
and the result object which will feature any entry one would expect according to the OP's acceptance-criteria and the additional renaming.
function collectSerialEntry({ renaming, result }, [propName, value]) {
const regXSerialKey = /(?<key>.*?\D)(?<idx>\d*$)/;
let { key = null, idx = null } = regXSerialKey
.exec(propName)
?.groups ?? {};
if (key !== null) {
key = renaming[key] ?? key;
if (idx !== null) {
idx = parseInt(idx, 10);
idx = Number.isNaN(idx) && 1 || idx;
// - create and/or access the `key` related array
// and assign the value according to the
// retrieved index.
(result[key] ??= [])[idx - 1] = value;
} else {
// - assign the value according to its current key.^
result[key] = value;
}
}
return { renaming, result };
}
const sampleData = {
strIngredient1: 'Olive Oil',
strIngredient2: 'Onion',
strIngredient3: 'Chicken Breast',
strMeasure1: '100 ml',
strMeasure2: '2',
strMeasure3: '4',
strDescription: 'Start frying ... lorem ipsum dolor sit amet.',
};
const {
result: {
ingredientList,
measureList,
description,
},
} = Object
.entries(sampleData)
.reduce(collectSerialEntry, {
// - provide an initial value ...
renaming: {
strIngredient: 'ingredientList',
strMeasure: 'measureList',
strDescription: 'description',
},
// ... which features a lookup
// for renaming some or all of
// the original property-names ...
//
// ... and the `result` object
// which will feature any entry
// one would expect according to
// the OP's acceptance-criteria
// and the additional renaming.
result: {},
});
console.log(sampleData, ' => ', {
ingredientList,
measureList,
description,
});
.as-console-wrapper { min-height: 100%!important; top: 0; }

Tick Tack Toe: Filter multidimensional array in JavaScript

I am learning to filter nested arrays in javascript and I believe this example can help me to understand it better. below is the object.
const game = () => {
return {
player1: {
username: 'ABC',
playingAs: 'X'
},
player2: {
username: 'XYZ',
playingAs: '0'
},
board:
[
['✓', null, 'X'],
['X', '✓', null],
['✓', null, 'X']
]
}
};
my code is below
const {player1: {username: PlayerA, playingAs:PlayerAMark}, player2: {username: PlayerB, playingAs:PlayerBMark}} = game();
const {board: board} = game();
//is there any efficient way to do it without a loop using a filter?
for (const item in board) {
//One approach which comes to my mind is to loop. but that is what I don't think would be a learning curve for me.
}
I am trying something like this
const array = board;
for (const item in board) {
console.log(array.filter(innerArray => innerArray[item] !== 'X'));
}
I am focused on implementing some efficient way that can help me to return
Player1 marked 'X' at 3,1,3 and
Player 2 marked '0' at location at 0,2,1
I am only stuck with filtering this board multidimensional array.
I appreciate your help on this.
You can also use reduce function. We can have the desired output using this code:
function printForPlayer(player, board) {
var indexes = board.reduce((acc,row)=>[...row.reduce((acc2,item,index) => item==player.playingAs ? [...acc2,index+1] : acc2, []),...acc], [])
console.log(`${player.username} marked ${player.playingAs} at ${indexes.join(',')}`)
}
printForPlayer(game().player1, game().board);
printForPlayer(game().player2, game().board);
this is a bit complex, but it works without loop
One way is to loop over each row to create an array with all the places where the player has his own symbol.
Using reduce(), we can find all the indexes of the symobl of the current player, then after the loop, use join() to show it.
const data = {player1: {username: 'ABC', playingAs: 'X'}, player2: {username: 'XYZ', playingAs: '0'}, board: [['0', null, 'X'], ['X', '0', null], ['0', null, 'X'] ] };
const logPlayer = (player) => {
const ox = data[player].playingAs;
let indexes = [];
for (let row in data.board) {
const ii = data.board[row].reduce((prev, cur, i) => (cur === ox) ? [ ...prev, i ] : prev,[]);
if (ii.length > 0) ii.map(i => indexes.push(`${+row + 1}-${i + 1}`));
}
console.log(`${player} has placed an '${ox}' on the following places (row-column)`, indexes.join(", "));
}
logPlayer('player1');
logPlayer('player2');
player1 has placed an 'X' on the following places (row-column) 1-3, 2-1, 3-3
player2 has placed an '0' on the following places (row-column) 1-1, 2-2, 3-1
So here is my solution which worked for me.
First of all, null is always rendered as 0. So I hade to update my array as mentioned below
board:
[
['✓', null, 'X'],
['X', '✓', null],
['✓', null, 'X']
]
Secondly, after updating the board value in the array, the following solution worked for me.
const {
player1: {username: PlayerA, playingAs:PlayerAMark},
player2: {username: PlayerB, playingAs:PlayerBMark},
board: GameBoard
} = game();
let array = GameBoard.entries();
let tick = [];
let cross = [];
for (const [index,element] of array) {
tick.push(element.indexOf(PlayerBMark)+ 1);
cross.push(element.indexOf(PlayerAMark)+ 1);
}
console.log('Player ', PlayerA, ' Clicked ', PlayerAMark ,' at '+ cross);
console.log('Player ',PlayerB, ' Clicked ', PlayerBMark ,' at '+ tick);
Here is the result for this.
Player ABC Clicked X at 3,1,3
Player XYZ Clicked ✓ at 1,2,1
Thanks everyone for all the help. But, still I am looking for How we can improve its performance and avoided looping?
Thanks again.

Remove singular element from an object's key array

I have an object that has multiple keys and each of these keys has an array storing multiple elements. I want to be able to remove a specified element from the key's array.
I have tried using the delete keyword as well as the filter method, but I have been unsuccessful. I'm a total newbie to JS so I appreciate any assistance. Also, I want to do this using ONLY JavaScript, no libraries.
Here is the code where I am creating my object:
function add(task, weekdayDue) {
let capitalWeekday = weekdayDue.charAt(0).toUpperCase() +
weekdayDue.slice(1);
if (toDoList[capitalWeekday] === undefined) {
let subArr = [];
toDoList[capitalWeekday] = subArr.concat(task);
} else {
toDoList[capitalWeekday].push(task);
}
}
and here is the code as I have it now. Clearly it is not producing the correct result:
function remove(task, weekdayDue) {
let capitalWeekday = weekdayDue.charAt(0).toUpperCase() +
weekdayDue.slice(1);
delete toDoList.capitalWeekday[task]
//the below code is working; i want to send this to another
array
if (archivedList[capitalWeekday] === undefined) {
let subArr = [];
archivedList[capitalWeekday] = subArr.concat(task);
} else {
archivedList[capitalWeekday].push(task);
}
};
add('laundry', 'monday');
add('wash car', 'monday');
add ('vacuum', 'tuesday');
add('run errands', 'wednesday');
add('grocery shopping', 'wednesday');
// the output is: { Monday: [ 'laundry', 'wash car' ],
Tuesday: [ 'vacuum' ],
Wednesday: [ 'run errands', 'grocery shopping' ] }
Then let's say I want to remove 'wash car' from Monday I was trying:
remove('wash car', 'monday');
console.log(toDoList)
// The output is an empty object {}
I personally would refactor a bit your code, but I've worked a bit around it to fix some issues.
First of all, you shouldn't use delete for your scenario, because it will reset the item at the nth position of the array with the default value, which is undefined.
Usually, for that kind of operations, since you deal with strings, you rather take a look at the first occurrence of your item in the array, take its index, and use splice (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice) to actually remove the item from the array.
In this way, you end up with a clean array without invalid items in it.
Below is the working code (with the mentioned fixes) that does what you asked. As a side note, I would suggest you to avoid working with strings for such purposes, but I would rather tackle objects with unique ids, so that it's significantly easier to keep track of them between arrays and objects.
Additionally, there are some cases that you didn't think about, for instance I can think about calling remove by giving an invalid task, so you may work a bit around the code below to handle the case where taskIndex is -1 (meaning that no item was found with that index).
var toDoList = {}, archivedList = {};
function add(task, weekdayDue) {
let capitalWeekday = weekdayDue.charAt(0).toUpperCase() + weekdayDue.slice(1);
if (toDoList[capitalWeekday] === undefined) {
let subArr = [];
toDoList[capitalWeekday] = subArr.concat(task);
} else {
toDoList[capitalWeekday].push(task);
}
}
function remove(task, weekdayDue) {
let capitalWeekday = weekdayDue.charAt(0).toUpperCase() + weekdayDue.slice(1);
let taskIndex = toDoList[capitalWeekday].indexOf(task);
toDoList[capitalWeekday].splice(taskIndex, 1);
//delete toDoList[capitalWeekday][taskIndex];
if (archivedList[capitalWeekday] === undefined) {
let subArr = [];
archivedList[capitalWeekday] = subArr.concat(task);
} else {
archivedList[capitalWeekday].push(task);
}
};
add('test', 'monday');
add('wash car', 'monday');
remove('wash car', 'monday');
console.log(toDoList);
console.log(archivedList);
You are on the right path. Maybe the trouble you had with filter is because filter will return a new Array and not modify the current one. You could update your remove function and replace the line:
delete toDoList.capitalWeekday[task]
with
toDoList.capitalWeekday = toDoList.capitalWeekday.filter((item) => {return item !== task});
function remove(task, weekdayDue) {
let capitalWeekday = weekdayDue.charAt(0).toUpperCase() +
weekdayDue.slice(1);
// Assign new array with all elements but task
toDoList[capitalWeekday] = toDoList[capitalWeekday].filter(i => i !== task)
};
add('foo'...
add('bar'...
"{
"Baz": [
"Foo",
"Bar"
]
}"
remove('foo'...
"{
"Baz": [
"Bar"
]
}"

How to declare a Hash/Dictionary of Array

I have a program that pushes values into one data structure like this:
if(symbolType == "C" || symbolType == "P") // The calls and puts
stocks.push({
symbol: symbol,
undsymbol: undSymbol,
open: 0,
type: symbolType,
expiry: expiry,
days: days,
strike: strike
});
}
else // The stock
{
stocks.push({
symbol: symbol,
open: 0,
type: symbolType
});
}
So this is the key: NOT A STRING!
{
symbol: symbol,
open: 0,
type: symbolType
}
And the values of which are many look like this:
{
symbol: symbol,
undsymbol: undSymbol,
open: 0,
type: symbolType,
expiry: expiry,
days: days,
strike: strike
}
The problem is that stocks and calls and puts are being put into one collection. Instead, I want to add the the stocks and their corresponding calls and puts into a dictionary/map, where the stocks are the keys, and the calls and puts get pushed into an array indexed by it's stock.
At the end, I want to be able to iterate and get the keys and values.
How do I declare this object
Index into it to see if the key[stock] already exists, if it doesn't add it with an empty array.
If I get a "C" or "P", I want to get the corresponding array that holds the Calls/Puts for this key [stock] and push the call/put into the array.
Initially I thought the declaration was something like this:
var stockCallsPutDict = {[]}
stockCallsPutDict[stock] = [];
stockCallsPut[stock].push(call);
// Pretty print the dict of keys and its options =
stockCallsPutDict.forEach(function kvp) {
...
}
If ES6 is an option, you can either build an object yourself or use a Map.
Here's some quick code I came up with:
const stocks = {};
const addCallAndPut = callAndPut => {
const symbol = callAndPut.symbol;
if (!stocks[symbol]) {
stocks[symbol] = [];
}
stocks[symbol].push(callAndPut);
}
const showStuff = () => {
for (const symbol in stocks) {
// output stuff using stocks[symbol]
}
}
OR WITH A MAP
const stocks = new Map();
// basic implementation
const addCallAndPut = callAndPut => {
const stockCallsAndPuts = stocks.get(callAndPut.symbol) || [];
stockCallsAndPuts.push(callAndPut);
stock.set(callAndPut.symbol, stockCallsAndPuts);
}
There are a few ways to go about this, and the best depends on how the data needs to be processed later, but from your description I'd go with something along the lines of
var stocks = {};
var stockCallsPut = {};
// loop over stocks and actions
if (!(symbol in stocks)) {
stocks[symbol] = [];
}
if (!(symbol in stockCallsPut)) {
stockCallsPut[symbol] = {};
}
if (!(symbolType in stockCallsPut[symbol])) {
stockCallsPut[symbol][symbolType] = [];
}
// accumulated stock json items here
stocks[symbol].push(new_stock_item);
// accumulated push/call json items of stock here
stockCallsPut[symbol][symbolType].push(new_action);
I'm still not sure I actually understood what your data looks like, but sounds kind of like this to me:
// Not sure if data is an object or array
var data = {
'one': {
'name': 'one-somename',
'number': 'one-somenumber',
'symbol': 'C'
},
'two': {
'name': 'two-somename',
'number': 'two-somenumber',
'symbol': 'P'
},
'three': {
'name': 'three-somename',
'number': 'three-somenumber',
'symbol': 'C'
}
};
var stocks = {};
for (var name in data) {
// It sounded like you wanted a call/put array for each object but I'm not sure if that's true since it wouldn't be possible... if so can just divide this part up into it's appropriate place in the if statement below
// Checking that the property is set on the object, if it is, it uses itself, otherwise it adds it with the call/put arrays created
stocks[name] = stocks[name] ? stocks[name] : {'calls': [], 'puts': []};
var type;
if (data[name]['symbol'] === 'C') {
type = 'calls';
} else if (data[name]['symbol'] === 'P') {
type = 'puts';
}
stocks[name][type].push(data[name]);
}

lodash/underscore; compare two objects and remove duplicates

As you can see in the image below, I have some returned json data with three objects; each contains a clients id => data.
exact_match : {104}
match_4 : {104, 103}
match_2 : {104, 103, 68}
How can I "trim" or remove the duplicate objects based on previous ones? something like:
exact_match : {104}
match_4 : {103}
match_2 : {68}
I tried _.difference but did not work (Maybe because it is for arrays not objects?):
var exact_match = data.exact_match,
match_four_digits = _.difference(data.match_4, data.exact_match),
match_two_digits = _.difference(data.match_2, data.exact_match, data.match_4),
Any help would be appreciated :)
Update
I need that the returned value has the same object data instead of a new array :)
It looks like you want to diff keys (or rather, it'd be efficient to — _.keys)
_.difference(
_.keys({104: 1, 102: 3, 101: 0}), // ["104", "102", "101"]
_.keys({104: 1, 102: 3}) // ["104", "102"]
)
// [ "101" ]
Or, you could always convert your object to an array of pairs if you want to compare within the objects too (_.pairs):
_.difference(
_.pairs({104: 1, 102: 3, 101: 0}), // [["104",1], ["102",3], ["101",0]]
_.pairs({104: 1, 102: 2}) // [["104",1], ["102",2]]
)
// [["102", 3], ["101", 0]]
I would create a map called unique, e.g. var unique = {}; and then iterate over each key in your data and check if it's in unique. If it is in unique, delete the entry associated with that key, thus removing duplicates.
You could pull this check out as an alreadyFound method:
var alreadyFound = function (key) {
if (!(key in unique)) {
unique[key] = true;
return false;
}
return true;
};
Then iterate over your data and check alreadyFound(key) for key in your data, and delete the key if alreadyFound(key) returns true.
You could go messing with lodash/underscore methods but those might be inefficient depending on how you use them (and how they're implemented) and this should operate in linear time.
It looks like for your specific use case the full solution would be something like:
var unique = {};
// Assume I copy and pasted alreadyFound here
var alreadyFound = ...;
for (var object in data) {
// Iterate through ids in each object in data
for (var id in object) {
// Remove this entry if it's already found
if (alreadyFound(id)) {
delete object[id];
}
}
}
Thanks guys for the answers, I really appreciate your time.
I searched further and found this post by Lodash developer that helped me came up with this snippet;
var data = {
exact_match: {
104: {
supplier_id: 104
}
},
match_four_digits: {
104: {
supplier_id: 104
},
68: {
supplier_id: 68
}
},
match_two_digits: {
104: {
supplier_id: 104
},
68: {
supplier_id: 68
},
103: {
supplier_id: 103
},
999: {
supplier_id: 999
}
}
};
var arr_match_four_digits = _.difference(_.keys(data.match_four_digits), _.keys(data.exact_match));
var arr_match_two_digits = _.difference(_.keys(data.match_two_digits), _.keys(data.match_four_digits), _.keys(data.exact_match));
$('#output1').html(JSON.stringify(data));
$('#output2').html(JSON.stringify(_.pick(data.match_four_digits, arr_match_four_digits)));
$('#output3').html(JSON.stringify(_.pick(data.match_two_digits, arr_match_two_digits)));
<script src="https://cdn.rawgit.com/lodash/lodash/3.3.1/lodash.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
data
<pre><code><div id="output1"></div></code></pre>
arr_match_four_digits
<pre><code><div id="output2"></div></code></pre>
match_two_digits
<pre><code><div id="output3"></div></code></pre>

Categories