8
$\begingroup$

Consider the following the list and association.

list1 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
asc = <|1 -> a, 4 -> b, 7 -> c, 10 -> d|>;

I would like to create an association which will have the keys same as list1 and values based on the upper key's value of asc. For example, asc does not have 2 key, so I would want to add an element 2 -> asc[4] i.e., 2 -> b and so on...

Required output is

<|1 -> a, 2 -> b, 3 -> b, 4 -> b, 5 -> c, 6 -> c, 7 -> c, 9 -> d, 10 -> d|>

The following is what I have tried.

f[n_] := If[! MemberQ[Keys[asc], n], asc[SelectFirst[Keys[asc], # > n &]], asc[n]]
Thread[list1 -> f /@ list1] // Association

Is there any elegant way to do this?

$\endgroup$
  • 2
    $\begingroup$ I think "better" is an improper attribute for code. Do you want the code to be more clear or more robust or shorter or more elegant or faster or ...? Or the other way round, why would you think your code needs improvement, it seems to do what you want, doesn't it? $\endgroup$ – Albert Retey Oct 15 '17 at 15:17
  • $\begingroup$ @AlbertRetey You are right. I should have clarified that. I want an elegant way. I was wondering if there was a Nearest[] type function except that it returns the upper key's value. $\endgroup$ – Anjan Kumar Oct 15 '17 at 15:41
5
$\begingroup$

Piecewise does what you need and here is a readable way to use it:

AssociationMap[
  Evaluate[
    Piecewise[KeyValueMap[Function[{k, v}, {v, # <= k}], asc]]
  ] &
, list1
]
$\endgroup$
  • $\begingroup$ Thank you for the neat solution. $\endgroup$ – Anjan Kumar Oct 15 '17 at 17:39
6
$\begingroup$

Another way (assuming that the keys are ordered)

AssociationThread[list1 -> Catenate[MapThread[ConstantArray,
         {Values[asc], Differences[Prepend[Keys[asc], 0]]}]]]

<|1 -> a, 2 -> b, 3 -> b, 4 -> b, 5 -> c, 6 -> c, 7 -> c, 8 -> d, 9 -> d, 10 -> d|>

$\endgroup$
3
$\begingroup$
Table[i -> asc[NestWhile[# + 1 &, i, ! MemberQ[Keys[asc], #] &]], {i,list1}] // Association
(* <|1 -> a, 2 -> b, 3 -> b, 4 -> b, 5 -> c, 6 -> c, 7 -> c, 8 -> d, 9 -> d, 10 -> d|> *)
$\endgroup$
3
$\begingroup$

Elegance is probably also in the eye of the beholder. Somewhat more objective (but of course not entirely) and in my opinion much more important considering code quality is clearness of intent. In that sense I like your original approach quite well. There are some minor improvements that I would make to make the code a bit more clear (and terse):

valueForThisOrNextExistingKey[known_][key_] := 
  known[SelectFirst[Keys[known], # >= key &]];

AssociationMap[valueForThisOrNextExistingKey[knownEntries], allKeys]

The changes are:

  • use (more) descriptive names for the function to apply to the list of all keys and the variables list1 (to allKeys) and asc (to knownEntries). There might be better names but these ones will at least be clear to me in the not so distant future.
  • use subvalues for the function definition, which makes the syntax for applying that function to allKeys somewhat less complicated. Supplying the known association as an argument also avoids the function to be dependent on the current value of a global variable.
  • use >= instead of > in SelectFirst criterion which renders the extra If unnecessary.
  • "stole" the AssociationMap from Kubas answer which makes the construction of the Association a bit more terse.

If you like the piecewise approach better, you'd only have to change the definition for valueForThisOrNextExistingKey to e.g.:

valueForThisOrNextExistingKey[known_][key_] := 
 Piecewise[KeyValueMap[Function[{k,v},{v, key <= k}],known]];

With that setup you can easily exchange that part of the implementation and should be in a good position for speed optimization if that would ever matter...

$\endgroup$
  • $\begingroup$ Thank you for the suggestions. $\endgroup$ – Anjan Kumar Oct 16 '17 at 13:08
  • $\begingroup$ I must say I'm flattered by being considered Szabolcs' socketpuppet account :) $\endgroup$ – Kuba Oct 16 '17 at 13:15
  • $\begingroup$ oh, sorry, reading too many answers in parallel :-). Just corrected... $\endgroup$ – Albert Retey Oct 16 '17 at 13:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.